| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- package tview
- import (
- "fmt"
- "strings"
- "github.com/gdamore/tcell/v2"
- )
- // listItem represents one item in a List.
- type listItem struct {
- 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.
- }
- // List displays rows of items, each of which can be selected. List items can be
- // shown as a single line or as two lines. They can be selected by pressing
- // their assigned shortcut key, navigating to them and pressing Enter, or
- // clicking on them with the mouse. The following key binds are available:
- //
- // - Down arrow / tab: Move down one item.
- // - Up arrow / backtab: Move up one item.
- // - Home: Move to the first item.
- // - End: Move to the last item.
- // - Page down: Move down one page.
- // - Page up: Move up one page.
- // - Enter / Space: Select the current item.
- // - Right / left: Scroll horizontally. Only if the list is wider than the
- // available space.
- //
- // See [List.SetChangedFunc] for a way to be notified when the user navigates
- // to a list item. See [List.SetSelectedFunc] for a way to be notified when a
- // list item was selected.
- //
- // See https://github.com/rivo/tview/wiki/List for an example.
- type List struct {
- *Box
- // The items of the list.
- items []*listItem
- // The index of the currently selected item.
- currentItem int
- // Whether or not to show the secondary item texts.
- showSecondaryText bool
- // The item main text style.
- mainTextStyle tcell.Style
- // The item secondary text style.
- secondaryTextStyle tcell.Style
- // The item shortcut text style.
- shortcutStyle tcell.Style
- // The style for selected items.
- selectedStyle tcell.Style
- // If true, the selection is only shown when the list has focus.
- selectedFocusOnly bool
- // If true, the entire row is highlighted when selected.
- highlightFullLine bool
- // Whether or not navigating the list will wrap around.
- wrapAround bool
- // The number of list items skipped at the top before the first item is
- // drawn.
- itemOffset int
- // The number of cells skipped on the left side of an item text. Shortcuts
- // are not affected.
- horizontalOffset int
- // Set to true if a currently visible item flows over the right border of
- // the box. This is set by the Draw() function. It determines the behaviour
- // of the right arrow key.
- overflowing bool
- // An optional function which is called when the user has navigated to a
- // list item.
- changed func(index int, mainText, secondaryText string, shortcut rune)
- // 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)
- // An optional function which is called when the user presses the Escape key.
- done func()
- }
- // NewList returns a new list.
- func NewList() *List {
- return &List{
- Box: NewBox(),
- showSecondaryText: true,
- wrapAround: true,
- mainTextStyle: tcell.StyleDefault.Foreground(Styles.PrimaryTextColor),
- secondaryTextStyle: tcell.StyleDefault.Foreground(Styles.TertiaryTextColor),
- shortcutStyle: tcell.StyleDefault.Foreground(Styles.SecondaryTextColor),
- selectedStyle: tcell.StyleDefault.Foreground(Styles.PrimitiveBackgroundColor).Background(Styles.PrimaryTextColor),
- }
- }
- // SetCurrentItem sets the currently selected item by its index, starting at 0
- // for the first item. 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 are clamped to the beginning/end.
- //
- // Calling this function triggers a "changed" event if the selection changes.
- func (l *List) SetCurrentItem(index int) *List {
- if index < 0 {
- index = len(l.items) + index
- }
- if index >= len(l.items) {
- index = len(l.items) - 1
- }
- if index < 0 {
- index = 0
- }
- if index != l.currentItem && l.changed != nil {
- item := l.items[index]
- l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
- }
- l.currentItem = index
- l.adjustOffset()
- return l
- }
- // GetCurrentItem returns the index of the currently selected list item,
- // starting at 0 for the first item.
- func (l *List) GetCurrentItem() int {
- return l.currentItem
- }
- // SetOffset sets the number of items to be skipped (vertically) as well as the
- // number of cells skipped horizontally when the list is drawn. Note that one
- // item corresponds to two rows when there are secondary texts. Shortcuts are
- // always drawn.
- //
- // These values may change when the list is drawn to ensure the currently
- // selected item is visible and item texts move out of view. Users can also
- // modify these values by interacting with the list.
- func (l *List) SetOffset(items, horizontal int) *List {
- l.itemOffset = items
- l.horizontalOffset = horizontal
- return l
- }
- // GetOffset returns the number of items skipped while drawing, as well as the
- // number of cells item text is moved to the left. See also SetOffset() for more
- // information on these values.
- func (l *List) GetOffset() (int, int) {
- return l.itemOffset, l.horizontalOffset
- }
- // 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
- // are clamped to the beginning/end, i.e. unless the list is empty, an item is
- // always removed.
- //
- // The currently selected item is shifted accordingly. If it is the one that is
- // removed, a "changed" event is fired, unless no items are left.
- func (l *List) RemoveItem(index int) *List {
- if len(l.items) == 0 {
- return l
- }
- // Adjust index.
- if index < 0 {
- index = len(l.items) + index
- }
- if index >= len(l.items) {
- index = len(l.items) - 1
- }
- if index < 0 {
- index = 0
- }
- // Remove item.
- l.items = append(l.items[:index], l.items[index+1:]...)
- // If there is nothing left, we're done.
- if len(l.items) == 0 {
- return l
- }
- // Shift current item.
- previousCurrentItem := l.currentItem
- if l.currentItem > index || l.currentItem == len(l.items) {
- l.currentItem--
- }
- // Fire "changed" event for removed items.
- if previousCurrentItem == index && l.changed != nil {
- item := l.items[l.currentItem]
- l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
- }
- return l
- }
- // SetMainTextColor sets the color of the items' main text.
- func (l *List) SetMainTextColor(color tcell.Color) *List {
- l.mainTextStyle = l.mainTextStyle.Foreground(color)
- return l
- }
- // SetMainTextStyle sets the style of the items' main text. Note that the
- // background color is ignored in order not to override the background color of
- // the list itself.
- func (l *List) SetMainTextStyle(style tcell.Style) *List {
- l.mainTextStyle = style
- return l
- }
- // SetSecondaryTextColor sets the color of the items' secondary text.
- func (l *List) SetSecondaryTextColor(color tcell.Color) *List {
- l.secondaryTextStyle = l.secondaryTextStyle.Foreground(color)
- return l
- }
- // SetSecondaryTextStyle sets the style of the items' secondary text. Note that
- // the background color is ignored in order not to override the background color
- // of the list itself.
- func (l *List) SetSecondaryTextStyle(style tcell.Style) *List {
- l.secondaryTextStyle = style
- return l
- }
- // SetShortcutColor sets the color of the items' shortcut.
- func (l *List) SetShortcutColor(color tcell.Color) *List {
- l.shortcutStyle = l.shortcutStyle.Foreground(color)
- return l
- }
- // SetShortcutStyle sets the style of the items' shortcut. Note that the
- // background color is ignored in order not to override the background color of
- // the list itself.
- func (l *List) SetShortcutStyle(style tcell.Style) *List {
- l.shortcutStyle = style
- return l
- }
- // SetSelectedTextColor sets the text color of selected items. Note that the
- // color of main text characters that are different from the main text color
- // (e.g. color tags) is maintained.
- func (l *List) SetSelectedTextColor(color tcell.Color) *List {
- l.selectedStyle = l.selectedStyle.Foreground(color)
- return l
- }
- // SetSelectedBackgroundColor sets the background color of selected items.
- func (l *List) SetSelectedBackgroundColor(color tcell.Color) *List {
- l.selectedStyle = l.selectedStyle.Background(color)
- return l
- }
- // SetSelectedStyle sets the style of the selected items. Note that the color of
- // main text characters that are different from the main text color (e.g. color
- // tags) is maintained.
- func (l *List) SetSelectedStyle(style tcell.Style) *List {
- l.selectedStyle = style
- return l
- }
- // SetSelectedFocusOnly sets a flag which determines when the currently selected
- // list item is highlighted. If set to true, selected items are only highlighted
- // when the list has focus. If set to false, they are always highlighted.
- func (l *List) SetSelectedFocusOnly(focusOnly bool) *List {
- l.selectedFocusOnly = focusOnly
- return l
- }
- // SetHighlightFullLine sets a flag which determines whether the colored
- // background of selected items spans the entire width of the view. If set to
- // true, the highlight spans the entire view. If set to false, only the text of
- // the selected item from beginning to end is highlighted.
- func (l *List) SetHighlightFullLine(highlight bool) *List {
- l.highlightFullLine = highlight
- return l
- }
- // ShowSecondaryText determines whether or not to show secondary item texts.
- func (l *List) ShowSecondaryText(show bool) *List {
- l.showSecondaryText = show
- return l
- }
- // SetWrapAround sets the flag that determines whether navigating the list will
- // wrap around. That is, navigating downwards on the last item will move the
- // selection to the first item (similarly in the other direction). If set to
- // false, the selection won't change when navigating downwards on the last item
- // or navigating upwards on the first item.
- func (l *List) SetWrapAround(wrapAround bool) *List {
- l.wrapAround = wrapAround
- return l
- }
- // 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.
- //
- // 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 {
- l.changed = handler
- return l
- }
- // 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 {
- l.selected = handler
- return l
- }
- // SetDoneFunc sets a function which is called when the user presses the Escape
- // key.
- func (l *List) SetDoneFunc(handler func()) *List {
- l.done = handler
- return l
- }
- // 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)
- return l
- }
- // InsertItem adds a new item to the list at the specified index. An index of 0
- // will insert the item at the beginning, an index of 1 before the second item,
- // and so on. An index of GetItemCount() or higher will insert the item at the
- // end of the list. Negative indices are also allowed: An index of -1 will
- // insert the item at the end of the list, an index of -2 before the last item,
- // and so on. An index of -GetItemCount()-1 or lower will insert the item at the
- // beginning.
- //
- // An item has a main text which will be highlighted when selected. It also has
- // a secondary text which is shown underneath the main text (if it is set to
- // visible) but which may remain empty.
- //
- // The shortcut is a key binding. If the specified rune is entered, the item
- // is selected immediately. Set to 0 for no binding.
- //
- // The "selected" callback will be invoked when the user selects the item. You
- // may provide nil if no such callback is needed or if all events are handled
- // through the selected callback set with SetSelectedFunc().
- //
- // 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 {
- item := &listItem{
- MainText: mainText,
- SecondaryText: secondaryText,
- Shortcut: shortcut,
- Selected: selected,
- }
- // Shift index to range.
- if index < 0 {
- index = len(l.items) + index + 1
- }
- if index < 0 {
- index = 0
- } else if index > len(l.items) {
- index = len(l.items)
- }
- // Shift current item.
- if l.currentItem < len(l.items) && l.currentItem >= index {
- l.currentItem++
- }
- // Insert item (make space for the new item, then shift and insert).
- l.items = append(l.items, nil)
- if index < len(l.items)-1 { // -1 because l.items has already grown by one item.
- copy(l.items[index+1:], l.items[index:])
- }
- l.items[index] = item
- // Fire a "change" event for the first item in the list.
- if len(l.items) == 1 && l.changed != nil {
- item := l.items[0]
- l.changed(0, item.MainText, item.SecondaryText, item.Shortcut)
- }
- return l
- }
- // GetItemCount returns the number of items in the list.
- func (l *List) GetItemCount() int {
- return len(l.items)
- }
- // GetItemText returns an item's texts (main and secondary). Panics if the index
- // is out of range.
- func (l *List) GetItemText(index int) (main, secondary string) {
- return l.items[index].MainText, l.items[index].SecondaryText
- }
- // SetItemText sets an item's main and secondary text. Panics if the index is
- // out of range.
- func (l *List) SetItemText(index int, main, secondary string) *List {
- item := l.items[index]
- item.MainText = main
- item.SecondaryText = secondary
- return l
- }
- // FindItems searches the main and secondary texts for the given strings and
- // returns a list of item indices in which those strings are found. One of the
- // two search strings may be empty, it will then be ignored. Indices are always
- // returned in ascending order.
- //
- // If mustContainBoth is set to true, mainSearch must be contained in the main
- // text AND secondarySearch must be contained in the secondary text. If it is
- // false, only one of the two search strings must be contained.
- //
- // Set ignoreCase to true for case-insensitive search.
- func (l *List) FindItems(mainSearch, secondarySearch string, mustContainBoth, ignoreCase bool) (indices []int) {
- if mainSearch == "" && secondarySearch == "" {
- return
- }
- if ignoreCase {
- mainSearch = strings.ToLower(mainSearch)
- secondarySearch = strings.ToLower(secondarySearch)
- }
- for index, item := range l.items {
- mainText := item.MainText
- secondaryText := item.SecondaryText
- if ignoreCase {
- mainText = strings.ToLower(mainText)
- secondaryText = strings.ToLower(secondaryText)
- }
- // strings.Contains() always returns true for a "" search.
- mainContained := strings.Contains(mainText, mainSearch)
- secondaryContained := strings.Contains(secondaryText, secondarySearch)
- if mustContainBoth && mainContained && secondaryContained ||
- !mustContainBoth && (mainText != "" && mainContained || secondaryText != "" && secondaryContained) {
- indices = append(indices, index)
- }
- }
- return
- }
- // Clear removes all items from the list.
- func (l *List) Clear() *List {
- l.items = nil
- l.currentItem = 0
- return l
- }
- // Draw draws this primitive onto the screen.
- func (l *List) Draw(screen tcell.Screen) {
- l.Box.DrawForSubclass(screen, l)
- // Determine the dimensions.
- x, y, width, height := l.GetInnerRect()
- bottomLimit := y + height
- _, totalHeight := screen.Size()
- if bottomLimit > totalHeight {
- bottomLimit = totalHeight
- }
- // Do we show any shortcuts?
- var showShortcuts bool
- for _, item := range l.items {
- if item.Shortcut != 0 {
- showShortcuts = true
- x += 4
- width -= 4
- break
- }
- }
- if l.horizontalOffset < 0 {
- l.horizontalOffset = 0
- }
- // Draw the list items.
- var (
- maxWidth int // The maximum printed item width.
- overflowing bool // Whether a text's end exceeds the right border.
- )
- for index, item := range l.items {
- if index < l.itemOffset {
- continue
- }
- if y >= bottomLimit {
- break
- }
- // Shortcuts.
- if showShortcuts && item.Shortcut != 0 {
- printWithStyle(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 0, 4, AlignRight, l.shortcutStyle, true)
- }
- // Main text.
- _, printedWidth, _, end := printWithStyle(screen, item.MainText, x, y, l.horizontalOffset, width, AlignLeft, l.mainTextStyle, true)
- if printedWidth > maxWidth {
- maxWidth = printedWidth
- }
- if end < len(item.MainText) {
- overflowing = true
- }
- // Background color of selected text.
- if index == l.currentItem && (!l.selectedFocusOnly || l.HasFocus()) {
- textWidth := width
- if !l.highlightFullLine {
- if w := TaggedStringWidth(item.MainText); w < textWidth {
- textWidth = w
- }
- }
- mainTextColor, _, _ := l.mainTextStyle.Decompose()
- for bx := 0; bx < textWidth; bx++ {
- m, c, style, _ := screen.GetContent(x+bx, y)
- fg, _, _ := style.Decompose()
- style = l.selectedStyle
- if fg != mainTextColor {
- style = style.Foreground(fg)
- }
- screen.SetContent(x+bx, y, m, c, style)
- }
- }
- y++
- if y >= bottomLimit {
- break
- }
- // Secondary text.
- if l.showSecondaryText {
- _, printedWidth, _, end := printWithStyle(screen, item.SecondaryText, x, y, l.horizontalOffset, width, AlignLeft, l.secondaryTextStyle, true)
- if printedWidth > maxWidth {
- maxWidth = printedWidth
- }
- if end < len(item.SecondaryText) {
- overflowing = true
- }
- y++
- }
- }
- // We don't want the item text to get out of view. If the horizontal offset
- // is too high, we reset it and redraw. (That should be about as efficient
- // as calculating everything up front.)
- if l.horizontalOffset > 0 && maxWidth < width {
- l.horizontalOffset -= width - maxWidth
- l.Draw(screen)
- }
- l.overflowing = overflowing
- }
- // adjustOffset adjusts the vertical offset to keep the current selection in
- // view.
- func (l *List) adjustOffset() {
- _, _, _, height := l.GetInnerRect()
- if height == 0 {
- return
- }
- if l.currentItem < l.itemOffset {
- l.itemOffset = l.currentItem
- } else if l.showSecondaryText {
- if 2*(l.currentItem-l.itemOffset) >= height-1 {
- l.itemOffset = (2*l.currentItem + 3 - height) / 2
- }
- } else {
- if l.currentItem-l.itemOffset >= height {
- l.itemOffset = l.currentItem + 1 - height
- }
- }
- }
- // InputHandler returns the handler for this primitive.
- func (l *List) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
- return l.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
- if event.Key() == tcell.KeyEscape {
- if l.done != nil {
- l.done()
- }
- return
- } else if len(l.items) == 0 {
- return
- }
- previousItem := l.currentItem
- switch key := event.Key(); key {
- case tcell.KeyTab, tcell.KeyDown:
- l.currentItem++
- case tcell.KeyBacktab, tcell.KeyUp:
- l.currentItem--
- case tcell.KeyRight:
- if l.overflowing {
- l.horizontalOffset += 2 // We shift by 2 to account for two-cell characters.
- } else {
- l.currentItem++
- }
- case tcell.KeyLeft:
- if l.horizontalOffset > 0 {
- l.horizontalOffset -= 2
- } else {
- l.currentItem--
- }
- case tcell.KeyHome:
- l.currentItem = 0
- case tcell.KeyEnd:
- l.currentItem = len(l.items) - 1
- case tcell.KeyPgDn:
- _, _, _, height := l.GetInnerRect()
- l.currentItem += height
- if l.currentItem >= len(l.items) {
- l.currentItem = len(l.items) - 1
- }
- case tcell.KeyPgUp:
- _, _, _, height := l.GetInnerRect()
- l.currentItem -= height
- if l.currentItem < 0 {
- l.currentItem = 0
- }
- case tcell.KeyEnter:
- if l.currentItem >= 0 && l.currentItem < len(l.items) {
- item := l.items[l.currentItem]
- if item.Selected != nil {
- item.Selected()
- }
- if l.selected != nil {
- l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
- }
- }
- case tcell.KeyRune:
- ch := event.Rune()
- if ch != ' ' {
- // It's not a space bar. Is it a shortcut?
- var found bool
- for index, item := range l.items {
- if item.Shortcut == ch {
- // We have a shortcut.
- found = true
- l.currentItem = index
- break
- }
- }
- if !found {
- break
- }
- }
- item := l.items[l.currentItem]
- if item.Selected != nil {
- item.Selected()
- }
- if l.selected != nil {
- l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
- }
- }
- if l.currentItem < 0 {
- if l.wrapAround {
- l.currentItem = len(l.items) - 1
- } else {
- l.currentItem = 0
- }
- } else if l.currentItem >= len(l.items) {
- if l.wrapAround {
- l.currentItem = 0
- } else {
- l.currentItem = len(l.items) - 1
- }
- }
- if l.currentItem != previousItem && l.currentItem < len(l.items) {
- if l.changed != nil {
- item := l.items[l.currentItem]
- l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
- }
- l.adjustOffset()
- }
- })
- }
- // indexAtPoint returns the index of the list item found at the given position
- // or a negative value if there is no such list item.
- func (l *List) indexAtPoint(x, y int) int {
- rectX, rectY, width, height := l.GetInnerRect()
- if rectX < 0 || rectX >= rectX+width || y < rectY || y >= rectY+height {
- return -1
- }
- index := y - rectY
- if l.showSecondaryText {
- index /= 2
- }
- index += l.itemOffset
- if index >= len(l.items) {
- return -1
- }
- return index
- }
- // MouseHandler returns the mouse handler for this primitive.
- func (l *List) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
- return l.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
- if !l.InRect(event.Position()) {
- return false, nil
- }
- // Process mouse event.
- switch action {
- case MouseLeftClick:
- setFocus(l)
- index := l.indexAtPoint(event.Position())
- if index != -1 {
- item := l.items[index]
- if item.Selected != nil {
- item.Selected()
- }
- if l.selected != nil {
- l.selected(index, item.MainText, item.SecondaryText, item.Shortcut)
- }
- if index != l.currentItem {
- if l.changed != nil {
- l.changed(index, item.MainText, item.SecondaryText, item.Shortcut)
- }
- l.adjustOffset()
- }
- l.currentItem = index
- }
- consumed = true
- case MouseScrollUp:
- if l.itemOffset > 0 {
- l.itemOffset--
- }
- consumed = true
- case MouseScrollDown:
- lines := len(l.items) - l.itemOffset
- if l.showSecondaryText {
- lines *= 2
- }
- if _, _, _, height := l.GetInnerRect(); lines > height {
- l.itemOffset++
- }
- consumed = true
- }
- return
- })
- }
|