Viewport now has customizable keybindings

This commit is contained in:
Christian Rocha 2021-09-02 14:00:37 -04:00
parent 9c70b6a216
commit 4aed4e0a88
2 changed files with 88 additions and 26 deletions

42
viewport/keymap.go Normal file
View File

@ -0,0 +1,42 @@
package viewport
import "github.com/charmbracelet/bubbles/key"
const spacebar = " "
// KeyMap defines the keybindings for the viewport. Note that you don't
// necessary need to use keybindings at all; the viewport can be controlled
// programmatically with methods like Model.LineDown(1). See the GoDocs for
// details.
type KeyMap struct {
PageDown key.Binding
PageUp key.Binding
HalfPageUp key.Binding
HalfPageDown key.Binding
Down key.Binding
Up key.Binding
}
// DefaultKeyMap returns a set of pager-like default keybindings.
func DefaultKeyMap() KeyMap {
return KeyMap{
PageDown: key.NewBinding(
key.WithKeys("pgdown", spacebar, "f"),
),
PageUp: key.NewBinding(
key.WithKeys("pgup", "b"),
),
HalfPageUp: key.NewBinding(
key.WithKeys("u", "ctrl+u"),
),
HalfPageDown: key.NewBinding(
key.WithKeys("d", "ctrl+d"),
),
Up: key.NewBinding(
key.WithKeys("up", "k"),
),
Down: key.NewBinding(
key.WithKeys("down", "j"),
),
}
}

View File

@ -4,18 +4,34 @@ import (
"math"
"strings"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
)
const (
spacebar = " "
mouseWheelDelta = 3
)
// NewModel returns a new model with the given width and height as well as
// default keymappings.
func NewModel(width, height int) Model {
return Model{
Width: width,
Height: height,
KeyMap: DefaultKeyMap(),
MouseWheelEnabled: true,
MouseWheelDelta: 3,
}
}
// Model is the Bubble Tea model for this viewport element.
type Model struct {
Width int
Height int
KeyMap KeyMap
// Whether or not to respond to the mouse. The mouse must be enabled in
// Bubble Tea for this to work. For details, see the Bubble Tea docs.
MouseWheelEnabled bool
// The number of lines the mouse wheel will scroll. By default, this is 3.
MouseWheelDelta int
// YOffset is the vertical scroll position.
YOffset int
@ -37,6 +53,11 @@ type Model struct {
lines []string
}
// Init exists to satisfy the tea.Model interface for composability purposes.
func (m Model) Init() tea.Cmd {
return nil
}
// AtTop returns whether or not the viewport is in the very top position.
func (m Model) AtTop() bool {
return m.YOffset <= 0
@ -227,54 +248,52 @@ func ViewUp(m Model, lines []string) tea.Cmd {
return tea.ScrollUp(lines, top, bottom)
}
// UPDATE
// Update runs the update loop with default keybindings similar to popular
// pagers. To define your own keybindings use the methods on Model (i.e.
// Model.LineDown()) and define your own update function.
// Update handles standard message-based viewport updates.
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
var cmd tea.Cmd
m, cmd = m.updateAsModel(msg)
return m, cmd
}
// Author's note: this method has been broken out to make it easier to
// potentially transition Update to satisfy tea.Model.
func (m Model) updateAsModel(msg tea.Msg) (Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
// Down one page
case "pgdown", spacebar, "f":
switch {
case key.Matches(msg, m.KeyMap.PageDown):
lines := m.ViewDown()
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
// Up one page
case "pgup", "b":
case key.Matches(msg, m.KeyMap.PageUp):
lines := m.ViewUp()
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
// Down half page
case "d", "ctrl+d":
case key.Matches(msg, m.KeyMap.HalfPageDown):
lines := m.HalfViewDown()
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
// Up half page
case "u", "ctrl+u":
case key.Matches(msg, m.KeyMap.HalfPageUp):
lines := m.HalfViewUp()
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
// Down one line
case "down", "j":
case key.Matches(msg, m.KeyMap.Down):
lines := m.LineDown(1)
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
// Up one line
case "up", "k":
case key.Matches(msg, m.KeyMap.Up):
lines := m.LineUp(1)
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
@ -282,15 +301,18 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
}
case tea.MouseMsg:
if !m.MouseWheelEnabled {
break
}
switch msg.Type {
case tea.MouseWheelUp:
lines := m.LineUp(mouseWheelDelta)
lines := m.LineUp(m.MouseWheelDelta)
if m.HighPerformanceRendering {
cmd = ViewUp(m, lines)
}
case tea.MouseWheelDown:
lines := m.LineDown(mouseWheelDelta)
lines := m.LineDown(m.MouseWheelDelta)
if m.HighPerformanceRendering {
cmd = ViewDown(m, lines)
}
@ -300,8 +322,6 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
return m, cmd
}
// VIEW
// View renders the viewport into a string.
func (m Model) View() string {
if m.HighPerformanceRendering {