Skip to content

Commit

Permalink
Added functionality to implement help.KeyMap interface (#185)
Browse files Browse the repository at this point in the history
* Added functionality to implement help.KeyMap interface

* Formatting

* Made additionalHelp... private. Integrated implementation with 'With..'

* Added short comments and fixed logic error in Help string

* Fixing linter issues

* Fixing linter issues

* Fix linter errors

* Fixing linter error
  • Loading branch information
MaximilianSoerenPollak authored Oct 31, 2024
1 parent 0a779ae commit f9795db
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 5 deletions.
49 changes: 48 additions & 1 deletion table/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,44 +30,91 @@ type KeyMap struct {
ScrollLeft key.Binding
}

// DefaultKeyMap returns a set of sensible defaults for controlling a focused table.
// DefaultKeyMap returns a set of sensible defaults for controlling a focused table with help text.
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("<space>/enter", "select row"),
),
PageDown: key.NewBinding(
key.WithKeys("right", "l", "pgdown"),
key.WithHelp("→/h/page down", "next page"),
),
PageUp: key.NewBinding(
key.WithKeys("left", "h", "pgup"),
key.WithHelp("←/h/page up", "previous 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 {
keyBinds := [][]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 {
keyBinds = append(keyBinds, m.additionalFullHelpKeys())
}

return keyBinds
}

// 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 {
keyBinds := []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 {
keyBinds = append(keyBinds, m.additionalShortHelpKeys()...)
}

return keyBinds
}
89 changes: 89 additions & 0 deletions table/keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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.
keys := []key.Binding{key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds"))}
model = model.WithAdditionalShortHelpKeys(keys)
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.
keys := []key.Binding{key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "Testing additional keybinds"))}
model = model.WithAdditionalFullHelpKeys(keys)
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)
}
18 changes: 16 additions & 2 deletions table/model.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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

Expand Down
21 changes: 19 additions & 2 deletions table/options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package table

import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/lipgloss"
)
Expand Down Expand Up @@ -302,7 +303,6 @@ func (m Model) WithCurrentPage(currentPage int) Model {
if m.pageSize == 0 || currentPage == m.CurrentPage() {
return m
}

if currentPage < 1 {
currentPage = 1
} else {
Expand All @@ -312,7 +312,6 @@ func (m Model) WithCurrentPage(currentPage int) Model {
currentPage = maxPages
}
}

m.currentPage = currentPage - 1
m.rowCursorIndex = m.currentPage * m.pageSize

Expand Down Expand Up @@ -465,3 +464,21 @@ func (m Model) WithMultiline(multiline bool) Model {

return m
}

// WithAdditionalShortHelpKeys enables you to add more keybindings to the 'short help' view.
func (m Model) WithAdditionalShortHelpKeys(keys []key.Binding) Model {
m.additionalShortHelpKeys = func() []key.Binding {
return keys
}

return m
}

// WithAdditionalFullHelpKeys enables you to add more keybindings to the 'full help' view.
func (m Model) WithAdditionalFullHelpKeys(keys []key.Binding) Model {
m.additionalFullHelpKeys = func() []key.Binding {
return keys
}

return m
}

0 comments on commit f9795db

Please sign in to comment.