refactor(picker): rename Picker to Model (#770)
This commit is contained in:
parent
3f8a6533da
commit
ee8439bc0a
5 changed files with 169 additions and 169 deletions
|
|
@ -16,7 +16,7 @@ type attachmentItem struct {
|
|||
}
|
||||
|
||||
type attachmentsPicker struct {
|
||||
*picker.Picker
|
||||
*picker.Model
|
||||
cfg *config.Config
|
||||
chatView *Model
|
||||
items []attachmentItem
|
||||
|
|
@ -26,7 +26,7 @@ var _ help.KeyMap = (*attachmentsPicker)(nil)
|
|||
|
||||
func newAttachmentsPicker(cfg *config.Config, chatView *Model) *attachmentsPicker {
|
||||
ap := &attachmentsPicker{
|
||||
Picker: picker.New(),
|
||||
Model: picker.NewModel(),
|
||||
cfg: cfg,
|
||||
chatView: chatView,
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ func (ap *attachmentsPicker) HandleEvent(event tcell.Event) tview.Command {
|
|||
return nil
|
||||
}
|
||||
|
||||
return ap.Picker.HandleEvent(event)
|
||||
return ap.Model.HandleEvent(event)
|
||||
}
|
||||
|
||||
func (ap *attachmentsPicker) ShortHelp() []keybind.Keybind {
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ import (
|
|||
)
|
||||
|
||||
type channelsPicker struct {
|
||||
*picker.Picker
|
||||
*picker.Model
|
||||
chatView *Model
|
||||
}
|
||||
|
||||
var _ help.KeyMap = (*channelsPicker)(nil)
|
||||
|
||||
func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker {
|
||||
cp := &channelsPicker{picker.New(), chatView}
|
||||
cp := &channelsPicker{picker.NewModel(), chatView}
|
||||
cp.Box = ui.ConfigureBox(tview.NewBox(), &cfg.Theme)
|
||||
// When a child of the parent flex is focused, the parent layout itself is not reported as focused.
|
||||
// Instead, the focused child (picker) is considered focused.
|
||||
|
|
@ -84,7 +84,7 @@ func (cp *channelsPicker) HandleEvent(event tcell.Event) tview.Command {
|
|||
cp.chatView.closePicker()
|
||||
return nil
|
||||
}
|
||||
return cp.Picker.HandleEvent(event)
|
||||
return cp.Model.HandleEvent(event)
|
||||
}
|
||||
|
||||
func (cp *channelsPicker) update() {
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ func newSelectedEvent(item Item) *SelectedEvent {
|
|||
return &SelectedEvent{Item: item}
|
||||
}
|
||||
|
||||
func (p *Picker) _select() tview.Command {
|
||||
index := p.list.Cursor()
|
||||
if index >= 0 && index < len(p.filtered) {
|
||||
item := p.filtered[index]
|
||||
func (m *Model) _select() tview.Command {
|
||||
index := m.list.Cursor()
|
||||
if index >= 0 && index < len(m.filtered) {
|
||||
item := m.filtered[index]
|
||||
return func() tcell.Event {
|
||||
return newSelectedEvent(item)
|
||||
}
|
||||
|
|
|
|||
159
pkg/picker/model.go
Normal file
159
pkg/picker/model.go
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
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 Picker struct {
|
||||
*flex.Model
|
||||
input *tview.InputField
|
||||
list *list.Model
|
||||
|
||||
keyMap *KeyMap
|
||||
|
||||
items Items
|
||||
filtered Items
|
||||
}
|
||||
|
||||
func New() *Picker {
|
||||
p := &Picker{
|
||||
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
|
||||
|
||||
p.input.
|
||||
SetChangedFunc(p.onInputChanged).
|
||||
SetLabel("> ").
|
||||
SetBorders(tview.BordersBottom).
|
||||
SetBorderSet(borderSet).
|
||||
SetBorderStyle(tcell.StyleDefault.Dim(true))
|
||||
|
||||
p.
|
||||
SetDirection(flex.DirectionRow).
|
||||
AddItem(p.input, inputHeight, 0, true).
|
||||
AddItem(p.list, 0, 1, false)
|
||||
|
||||
p.Update()
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Picker) setFilteredItems(filtered Items) {
|
||||
p.filtered = filtered
|
||||
|
||||
p.list.SetBuilder(func(index int, cursor int) list.Item {
|
||||
if index < 0 || index >= len(p.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: p.filtered[index].Text, Style: style}}})
|
||||
})
|
||||
|
||||
if len(filtered) == 0 {
|
||||
p.list.SetCursor(-1)
|
||||
} else {
|
||||
p.list.SetCursor(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Picker) SetKeyMap(keyMap *KeyMap) {
|
||||
p.keyMap = keyMap
|
||||
}
|
||||
|
||||
// SetScrollBarVisibility sets when the picker's list scrollBar is rendered.
|
||||
func (p *Picker) SetScrollBarVisibility(visibility list.ScrollBarVisibility) {
|
||||
p.list.SetScrollBarVisibility(visibility)
|
||||
}
|
||||
|
||||
// SetScrollBar sets the scrollBar primitive used by the picker's list.
|
||||
func (p *Picker) SetScrollBar(scrollBar *tview.ScrollBar) {
|
||||
p.list.SetScrollBar(scrollBar)
|
||||
}
|
||||
|
||||
func (p *Picker) ClearInput() {
|
||||
p.input.SetText("")
|
||||
}
|
||||
|
||||
func (p *Picker) ClearList() {
|
||||
p.filtered = nil
|
||||
p.list.Clear()
|
||||
}
|
||||
|
||||
func (p *Picker) ClearItems() {
|
||||
p.items = nil
|
||||
p.filtered = nil
|
||||
}
|
||||
|
||||
func (p *Picker) AddItem(item Item) {
|
||||
p.items = append(p.items, item)
|
||||
}
|
||||
|
||||
func (p *Picker) Update() {
|
||||
p.ClearInput()
|
||||
p.onInputChanged("")
|
||||
}
|
||||
|
||||
func (p *Picker) onInputChanged(text string) {
|
||||
var fuzzied Items
|
||||
if text == "" {
|
||||
fuzzied = append(fuzzied, p.items...)
|
||||
} else {
|
||||
matches := fuzzy.FindFrom(text, p.items)
|
||||
for _, match := range matches {
|
||||
fuzzied = append(fuzzied, p.items[match.Index])
|
||||
}
|
||||
}
|
||||
p.setFilteredItems(fuzzied)
|
||||
}
|
||||
|
||||
func (p *Picker) HandleEvent(event tcell.Event) tview.Command {
|
||||
switch event := event.(type) {
|
||||
case *tview.KeyEvent:
|
||||
if p.keyMap != nil {
|
||||
switch {
|
||||
case keybind.Matches(event, p.keyMap.Up):
|
||||
p.list.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone))
|
||||
return nil
|
||||
case keybind.Matches(event, p.keyMap.Down):
|
||||
p.list.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone))
|
||||
return nil
|
||||
case keybind.Matches(event, p.keyMap.Top):
|
||||
p.list.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone))
|
||||
return nil
|
||||
case keybind.Matches(event, p.keyMap.Bottom):
|
||||
p.list.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone))
|
||||
return nil
|
||||
|
||||
case keybind.Matches(event, p.keyMap.Select):
|
||||
return p._select()
|
||||
case keybind.Matches(event, p.keyMap.Cancel):
|
||||
return cancel()
|
||||
}
|
||||
}
|
||||
|
||||
return p.Model.HandleEvent(event)
|
||||
}
|
||||
return p.Model.HandleEvent(event)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue