From f8148d45735806a50fe79bd1d9e6ff00ac3630ae Mon Sep 17 00:00:00 2001 From: Maximilian Date: Wed, 9 Oct 2024 15:23:23 +0200 Subject: [PATCH] Added functionality to implement help.KeyMap interface --- table/keys.go | 43 ++++++++++++++++++++++++++++- table/keys_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++ table/model.go | 19 +++++++++++-- 3 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 table/keys_test.go diff --git a/table/keys.go b/table/keys.go index bc7d119..7061242 100644 --- a/table/keys.go +++ b/table/keys.go @@ -30,44 +30,85 @@ type KeyMap struct { ScrollLeft key.Binding } -// DefaultKeyMap returns a set of sensible defaults for controlling a focused table. +// FullHelp returns a all keys help views in several rows. Needed to fullfil the 'help.Model' interface + +// DefaultKeyMap returns a set of sensible defaults for controlling a focused table, with explenation help texts. func DefaultKeyMap() KeyMap { return KeyMap{ RowDown: key.NewBinding( key.WithKeys("down", "j"), + key.WithHelp("↓/j", "move down"), ), RowUp: key.NewBinding( key.WithKeys("up", "k"), + key.WithHelp("↑/k", "move up"), ), RowSelectToggle: key.NewBinding( key.WithKeys(" ", "enter"), + key.WithHelp("/enter", "select row"), ), PageDown: key.NewBinding( key.WithKeys("right", "l", "pgdown"), + key.WithHelp("→/h/page down", "Pervious Page"), ), PageUp: key.NewBinding( key.WithKeys("left", "h", "pgup"), + key.WithHelp("←/h/page up", "Next Page"), ), PageFirst: key.NewBinding( key.WithKeys("home", "g"), + key.WithHelp("home/g", "First Page"), ), PageLast: key.NewBinding( key.WithKeys("end", "G"), + key.WithHelp("end/G", "Last Page"), ), Filter: key.NewBinding( key.WithKeys("/"), + key.WithHelp("/", "filter"), ), FilterBlur: key.NewBinding( key.WithKeys("enter", "esc"), + key.WithHelp("enter/esc", "unfocus"), ), FilterClear: key.NewBinding( key.WithKeys("esc"), + key.WithHelp("esc", "clear filter"), ), ScrollRight: key.NewBinding( key.WithKeys("shift+right"), + key.WithHelp("shift+→", "scroll right"), ), ScrollLeft: key.NewBinding( key.WithKeys("shift+left"), + key.WithHelp("shift+←", "scroll left"), ), } } + + +// FullHelp returns a multi row view of all the helpkeys that are defined. Needed to fullfil the 'help.Model' interface +// Also appends all user defined extra keys to the help +func (m Model) FullHelp() [][]key.Binding { + kb := [][]key.Binding{ + {m.keyMap.RowDown, m.keyMap.RowUp, m.keyMap.RowSelectToggle}, + {m.keyMap.PageDown, m.keyMap.PageUp, m.keyMap.PageFirst, m.keyMap.PageLast}, + {m.keyMap.Filter, m.keyMap.FilterBlur, m.keyMap.FilterClear, m.keyMap.ScrollRight, m.keyMap.ScrollLeft}, + } + if m.AdditionalFullHelpKeys != nil { + kb = append(kb, m.AdditionalFullHelpKeys()) + } + return kb +} + +// ShortHelp just returns a single row of help views. Needed to fullfil the 'help.Model' interface +// Also appends all user defined extra keys to the help +func (m Model) ShortHelp() []key.Binding { + kb := []key.Binding{ + m.keyMap.RowDown, m.keyMap.RowUp, m.keyMap.RowSelectToggle, m.keyMap.PageDown, m.keyMap.PageUp, m.keyMap.Filter, m.keyMap.FilterBlur, m.keyMap.FilterClear, + } + if m.AdditionalShortHelpKeys != nil { + kb = append(kb, m.AdditionalShortHelpKeys()...) + } + return kb +} diff --git a/table/keys_test.go b/table/keys_test.go new file mode 100644 index 0000000..8e6b2b2 --- /dev/null +++ b/table/keys_test.go @@ -0,0 +1,68 @@ +package table + +import ( + "testing" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/stretchr/testify/assert" +) + +func TestKeyMapShortHelp(t *testing.T) { + + columns := []Column{NewColumn("c1", "Column1", 10)} + model := New(columns) + km := DefaultKeyMap() + model.WithKeyMap(km) + assert.Nil(t, model.AdditionalShortHelpKeys) + assert.Equal(t, model.ShortHelp(), []key.Binding{ + model.keyMap.RowDown, model.keyMap.RowUp, model.keyMap.RowSelectToggle, model.keyMap.PageDown, model.keyMap.PageUp, model.keyMap.Filter, model.keyMap.FilterBlur, model.keyMap.FilterClear}) + + // Testing if the 'adding of keys' works too. + model.AdditionalShortHelpKeys = func() []key.Binding { + return []key.Binding{ + key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds")), + } + } + assert.NotNil(t, model.AdditionalShortHelpKeys) + assert.Equal(t, model.ShortHelp(), []key.Binding{ + model.keyMap.RowDown, model.keyMap.RowUp, model.keyMap.RowSelectToggle, model.keyMap.PageDown, model.keyMap.PageUp, model.keyMap.Filter, model.keyMap.FilterBlur, model.keyMap.FilterClear, key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds"))}) + +} + +func TestKeyMapFullHelp(t *testing.T) { + + columns := []Column{NewColumn("c1", "Column1", 10)} + model := New(columns) + km := DefaultKeyMap() + model.WithKeyMap(km) + assert.Nil(t, model.AdditionalFullHelpKeys) + assert.Equal(t, + model.FullHelp(), + [][]key.Binding{ + {model.keyMap.RowDown, model.keyMap.RowUp, model.keyMap.RowSelectToggle}, + {model.keyMap.PageDown, model.keyMap.PageUp, model.keyMap.PageFirst, model.keyMap.PageLast}, + {model.keyMap.Filter, model.keyMap.FilterBlur, model.keyMap.FilterClear, model.keyMap.ScrollRight, model.keyMap.ScrollLeft}}, + ) + // Testing if the 'adding of keys' works too. + model.AdditionalFullHelpKeys = func() []key.Binding { + return []key.Binding{ + key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds")), + } + } + assert.NotNil(t, model.AdditionalFullHelpKeys) + assert.Equal(t, + model.FullHelp(), + [][]key.Binding{ + {model.keyMap.RowDown, model.keyMap.RowUp, model.keyMap.RowSelectToggle}, + {model.keyMap.PageDown, model.keyMap.PageUp, model.keyMap.PageFirst, model.keyMap.PageLast}, + {model.keyMap.Filter, model.keyMap.FilterBlur, model.keyMap.FilterClear, model.keyMap.ScrollRight, model.keyMap.ScrollLeft}, + {key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds"))}}, + ) +} + +// Testing if Model actually implements the 'help.KeyMap' interface. +func TestKeyMapInterface(t *testing.T) { + model := New(nil) + assert.Implements(t, (*help.KeyMap)(nil), model) +} diff --git a/table/model.go b/table/model.go index d5e8937..f2a6c7e 100644 --- a/table/model.go +++ b/table/model.go @@ -1,6 +1,7 @@ package table import ( + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -28,8 +29,21 @@ type Model struct { missingDataIndicator interface{} // Interaction - focused bool - keyMap KeyMap + focused bool + keyMap KeyMap + + // Taken from: 'Bubbles/List' + // Additional key mappings for the short and full help views. This allows + // you to add additional key mappings to the help menu without + // re-implementing the help component. Of course, you can also disable the + // list's help component and implement a new one if you need more + // flexibility. + // You have to supply a keybinding like this: + // key.NewBinding( key.WithKeys("shift+left"), key.WithHelp("shift+←", "scroll left")) + // It needs both 'WithKeys' and 'WithHelp' + AdditionalShortHelpKeys func() []key.Binding + AdditionalFullHelpKeys func() []key.Binding + selectableRows bool rowCursorIndex int @@ -128,3 +142,4 @@ func New(columns []Column) Model { func (m Model) Init() tea.Cmd { return nil } +