| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- package picker
- import (
- "github.com/ayn2op/tview"
- "github.com/ayn2op/tview/flex"
- "github.com/ayn2op/tview/keybind"
- "github.com/ayn2op/tview/list"
- "github.com/gdamore/tcell/v3"
- "github.com/sahilm/fuzzy"
- )
- // bottom border + value
- const inputHeight = 2
- type Model struct {
- *flex.Model
- input *tview.InputField
- list *list.Model
- keyMap *KeyMap
- items Items
- filtered Items
- }
- func NewModel() *Model {
- m := &Model{
- Model: flex.NewModel(),
- input: tview.NewInputField(),
- list: list.NewModel(),
- }
- // Show a horizontal bottom border to visually separate input from list.
- var borderSet tview.BorderSet
- borderSet.Bottom = tview.BoxDrawingsLightHorizontal
- borderSet.BottomLeft = borderSet.Bottom
- borderSet.BottomRight = borderSet.Bottom
- m.input.
- SetChangedFunc(m.onInputChanged).
- SetLabel("> ").
- SetBorders(tview.BordersBottom).
- SetBorderSet(borderSet).
- SetBorderStyle(tcell.StyleDefault.Dim(true))
- m.
- SetDirection(flex.DirectionRow).
- AddItem(m.input, inputHeight, 0, true).
- AddItem(m.list, 0, 1, false)
- m.Update()
- return m
- }
- func (m *Model) setFilteredItems(filtered Items) {
- m.filtered = filtered
- m.list.SetBuilder(func(index int, cursor int) list.Item {
- if index < 0 || index >= len(m.filtered) {
- return nil
- }
- style := tcell.StyleDefault
- if index == cursor {
- style = style.Reverse(true)
- }
- return tview.NewTextView().
- SetScrollable(false).
- SetWrap(false).
- SetWordWrap(false).
- SetTextStyle(style).
- SetLines([]tview.Line{{{Text: m.filtered[index].Text, Style: style}}})
- })
- if len(filtered) == 0 {
- m.list.SetCursor(-1)
- } else {
- m.list.SetCursor(0)
- }
- }
- func (m *Model) SetKeyMap(keyMap *KeyMap) {
- m.keyMap = keyMap
- }
- // SetScrollBarVisibility sets when the picker's list scrollBar is rendered.
- func (m *Model) SetScrollBarVisibility(visibility list.ScrollBarVisibility) {
- m.list.SetScrollBarVisibility(visibility)
- }
- // SetScrollBar sets the scrollBar primitive used by the picker's list.
- func (m *Model) SetScrollBar(scrollBar *tview.ScrollBar) {
- m.list.SetScrollBar(scrollBar)
- }
- func (m *Model) ClearInput() {
- m.input.SetText("")
- }
- func (m *Model) ClearList() {
- m.filtered = nil
- m.list.Clear()
- }
- func (m *Model) ClearItems() {
- m.items = nil
- m.filtered = nil
- }
- func (m *Model) AddItem(item Item) {
- m.items = append(m.items, item)
- }
- func (m *Model) Update() {
- m.ClearInput()
- m.onInputChanged("")
- }
- func (m *Model) onInputChanged(text string) {
- var fuzzied Items
- if text == "" {
- fuzzied = append(fuzzied, m.items...)
- } else {
- matches := fuzzy.FindFrom(text, m.items)
- for _, match := range matches {
- fuzzied = append(fuzzied, m.items[match.Index])
- }
- }
- m.setFilteredItems(fuzzied)
- }
- func (m *Model) HandleEvent(event tcell.Event) tview.Command {
- switch event := event.(type) {
- case *tview.KeyEvent:
- if m.keyMap != nil {
- switch {
- case keybind.Matches(event, m.keyMap.Up):
- m.list.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone))
- return nil
- case keybind.Matches(event, m.keyMap.Down):
- m.list.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone))
- return nil
- case keybind.Matches(event, m.keyMap.Top):
- m.list.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone))
- return nil
- case keybind.Matches(event, m.keyMap.Bottom):
- m.list.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone))
- return nil
- case keybind.Matches(event, m.keyMap.Select):
- return m._select()
- case keybind.Matches(event, m.keyMap.Cancel):
- return cancel()
- }
- }
- return m.Model.HandleEvent(event)
- }
- return m.Model.HandleEvent(event)
- }
|