forked from Mirrors/bubbletea
Refactor the keybinding stuff + add backspace
This commit is contained in:
parent
051a370769
commit
208f421470
|
@ -22,14 +22,14 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) {
|
||||
func update(message tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) {
|
||||
m, _ := mdl.(model)
|
||||
|
||||
switch message := msg.(type) {
|
||||
switch msg := message.(type) {
|
||||
|
||||
case tea.KeyPressMsg:
|
||||
switch message {
|
||||
case "ctrl+c":
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "break":
|
||||
fallthrough
|
||||
case "esc":
|
||||
fallthrough
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package main
|
||||
|
||||
// TODO: This code feels messy. Clean it up.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
@ -62,8 +64,8 @@ func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) {
|
|||
func updateChoices(msg tea.Msg, m Model) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
|
||||
case tea.KeyPressMsg:
|
||||
switch msg {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "j":
|
||||
fallthrough
|
||||
case "down":
|
||||
|
@ -85,7 +87,7 @@ func updateChoices(msg tea.Msg, m Model) (tea.Model, tea.Cmd) {
|
|||
fallthrough
|
||||
case "esc":
|
||||
fallthrough
|
||||
case "ctrl+c":
|
||||
case "break":
|
||||
return m, tea.Quit
|
||||
}
|
||||
|
||||
|
@ -102,13 +104,13 @@ func updateChoices(msg tea.Msg, m Model) (tea.Model, tea.Cmd) {
|
|||
func updateChosen(msg tea.Msg, m Model) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
|
||||
case tea.KeyPressMsg:
|
||||
switch msg {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q":
|
||||
fallthrough
|
||||
case "esc":
|
||||
fallthrough
|
||||
case "ctrl+c":
|
||||
case "break":
|
||||
return m, tea.Quit
|
||||
}
|
||||
|
||||
|
|
109
key.go
109
key.go
|
@ -7,61 +7,126 @@ import (
|
|||
)
|
||||
|
||||
// KeyPressMsg contains information about a keypress
|
||||
type KeyPressMsg string
|
||||
type KeyMsg Key
|
||||
|
||||
// Control keys
|
||||
// String returns a friendly name for a key
|
||||
func (k *KeyMsg) String() string {
|
||||
if k.Type == KeyRune {
|
||||
return string(k.Rune)
|
||||
} else if s, ok := keyNames[k.Type]; ok {
|
||||
return s
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsRune returns weather or not the key is a rune
|
||||
func (k *KeyMsg) IsRune() bool {
|
||||
if k.Type == KeyRune {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
Type KeyType
|
||||
Rune rune
|
||||
}
|
||||
|
||||
// KeyType indicates the key pressed
|
||||
type KeyType int
|
||||
|
||||
// Possible keys
|
||||
const (
|
||||
KeyBreak KeyType = iota
|
||||
KeyEnter
|
||||
KeyEscape
|
||||
KeyUp
|
||||
KeyDown
|
||||
KeyRight
|
||||
KeyLeft
|
||||
KeyUnitSeparator
|
||||
KeyBackspace
|
||||
KeyRune
|
||||
)
|
||||
|
||||
// Friendly key names
|
||||
var keyNames = map[KeyType]string{
|
||||
KeyBreak: "break",
|
||||
KeyEnter: "enter",
|
||||
KeyEscape: "esc",
|
||||
KeyUp: "up",
|
||||
KeyDown: "down",
|
||||
KeyRight: "right",
|
||||
KeyLeft: "left",
|
||||
KeyUnitSeparator: "us",
|
||||
KeyBackspace: "backspace",
|
||||
KeyRune: "rune",
|
||||
}
|
||||
|
||||
// Control keys. I know we could do this with an iota, but the values are very
|
||||
// specific, so we set the values explicitly to avoid any confusion
|
||||
const (
|
||||
keyETX = 3 // break, ctrl+c
|
||||
keyLF = 9 // line-feed, \n
|
||||
keyCR = 13 // carriage return, \r
|
||||
keyESC = 27
|
||||
keyESC = 27 // escape
|
||||
keyUS = 31 // unit separator
|
||||
keyDEL = 127 // delete. on most systems this is mapped to backspace, I hear
|
||||
)
|
||||
|
||||
var controlNames = map[int]string{
|
||||
keyETX: "ctrl+c",
|
||||
keyLF: "enter",
|
||||
keyCR: "enter",
|
||||
keyESC: "esc",
|
||||
keyUS: "us",
|
||||
// Mapping for control keys to friendly consts
|
||||
var controlKeys = map[int]KeyType{
|
||||
keyETX: KeyBreak,
|
||||
keyLF: KeyEnter,
|
||||
keyCR: KeyEnter,
|
||||
keyESC: KeyEscape,
|
||||
keyUS: KeyUnitSeparator,
|
||||
keyDEL: KeyBackspace,
|
||||
}
|
||||
|
||||
var sequenceNames = map[string]string{
|
||||
"\x1b[A": "up",
|
||||
"\x1b[B": "down",
|
||||
"\x1b[C": "right",
|
||||
"\x1b[D": "left",
|
||||
// Mapping for sequences to consts
|
||||
var sequences = map[string]KeyType{
|
||||
"\x1b[A": KeyUp,
|
||||
"\x1b[B": KeyDown,
|
||||
"\x1b[C": KeyRight,
|
||||
"\x1b[D": KeyLeft,
|
||||
}
|
||||
|
||||
// ReadKey reads keypress input from a TTY and returns a string representation
|
||||
// of a key
|
||||
func ReadKey(r io.Reader) (string, error) {
|
||||
func ReadKey(r io.Reader) (Key, error) {
|
||||
var buf [256]byte
|
||||
|
||||
// Read and block
|
||||
n, err := r.Read(buf[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
return Key{}, err
|
||||
}
|
||||
|
||||
// Get rune
|
||||
c, _ := utf8.DecodeRune(buf[:])
|
||||
if c == utf8.RuneError {
|
||||
return "", errors.New("no such rune")
|
||||
return Key{}, errors.New("no such rune")
|
||||
}
|
||||
|
||||
// Is it a control character?
|
||||
if n == 1 && c <= keyUS || c == keyDEL {
|
||||
if k, ok := controlKeys[n]; ok {
|
||||
return Key{Type: k}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 && c <= keyUS {
|
||||
if s, ok := controlNames[int(c)]; ok {
|
||||
return s, nil
|
||||
if k, ok := controlKeys[int(c)]; ok {
|
||||
return Key{Type: k}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Is it a special sequence, like an arrow key?
|
||||
if s, ok := sequenceNames[string(buf[:n])]; ok {
|
||||
return s, nil
|
||||
if k, ok := sequences[string(buf[:n])]; ok {
|
||||
return Key{Type: k}, nil
|
||||
}
|
||||
|
||||
// Nope, just a regular, ol' rune
|
||||
return string(c), nil
|
||||
return Key{Type: KeyRune, Rune: c}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue