mirror of
https://github.com/taigrr/bubbletea.git
synced 2026-04-02 02:59:09 -07:00
Support multi-character input (i.e. Chinese and Japanese IME)
This commit is contained in:
committed by
Christian Rocha
parent
eae4d66917
commit
fc37167e16
92
key.go
92
key.go
@@ -35,6 +35,11 @@ import (
|
||||
// fmt.Println("you pressed a!")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Note that Key.Runes will always contain at least one character, so you can
|
||||
// always safely call Key.Runes[0]. In most cases Key.Runes will only contain
|
||||
// one character, though certain input method editors (most notably Chinese
|
||||
// IMEs) can input multiple runes at once.
|
||||
type KeyMsg Key
|
||||
|
||||
// String returns a friendly name for a key.
|
||||
@@ -46,8 +51,8 @@ func (k *KeyMsg) String() (str string) {
|
||||
if k.Alt {
|
||||
str += "alt+"
|
||||
}
|
||||
if k.Type == KeyRune {
|
||||
str += string(k.Rune)
|
||||
if k.Type == KeyRunes {
|
||||
str += string(k.Runes)
|
||||
return str
|
||||
} else if s, ok := keyNames[int(k.Type)]; ok {
|
||||
str += s
|
||||
@@ -58,24 +63,24 @@ func (k *KeyMsg) String() (str string) {
|
||||
|
||||
// IsRune returns whether or not the key is a rune.
|
||||
func (k *KeyMsg) IsRune() bool {
|
||||
return k.Type == KeyRune
|
||||
return k.Type == KeyRunes
|
||||
}
|
||||
|
||||
// Key contains information about a keypress.
|
||||
type Key struct {
|
||||
Type KeyType
|
||||
Rune rune
|
||||
Alt bool
|
||||
Type KeyType
|
||||
Runes []rune
|
||||
Alt bool
|
||||
}
|
||||
|
||||
// KeyType indicates the key pressed, such as KeyEnter or KeyBreak or
|
||||
// KeyCtrlC. All other keys will be type KeyRune. To get the rune value, check
|
||||
// KeyCtrlC. All other keys will be type KeyRunes. To get the rune value, check
|
||||
// the Rune method on a Key struct, or use the Key.String() method:
|
||||
//
|
||||
// k := Key{Type: KeyRune, Rune: 'a', Alt: true}
|
||||
// if k.Type == KeyRune {
|
||||
// k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true}
|
||||
// if k.Type == KeyRunes {
|
||||
//
|
||||
// fmt.Println(k.Rune)
|
||||
// fmt.Println(k.Runes)
|
||||
// // Output: a
|
||||
//
|
||||
// fmt.Println(k.String())
|
||||
@@ -174,7 +179,7 @@ const (
|
||||
|
||||
// Other keys.
|
||||
const (
|
||||
KeyRune = -(iota + 1)
|
||||
KeyRunes = -(iota + 1)
|
||||
KeyUp
|
||||
KeyDown
|
||||
KeyRight
|
||||
@@ -224,7 +229,7 @@ var keyNames = map[int]string{
|
||||
keySP: "space",
|
||||
keyDEL: "backspace",
|
||||
|
||||
KeyRune: "rune",
|
||||
KeyRunes: "runes",
|
||||
KeyUp: "up",
|
||||
KeyDown: "down",
|
||||
KeyRight: "right",
|
||||
@@ -279,11 +284,11 @@ var hexes = map[string]Key{
|
||||
|
||||
// readInput reads keypress and mouse input from a TTY and returns a message
|
||||
// containing information about the key or mouse event accordingly.
|
||||
func readInput(r io.Reader) (Msg, error) {
|
||||
func readInput(input io.Reader) (Msg, error) {
|
||||
var buf [256]byte
|
||||
|
||||
// Read and block
|
||||
numBytes, err := r.Read(buf[:])
|
||||
numBytes, err := input.Read(buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -295,31 +300,48 @@ func readInput(r io.Reader) (Msg, error) {
|
||||
return MouseMsg(mouseEvent), nil
|
||||
}
|
||||
|
||||
hex := fmt.Sprintf("%x", buf[:numBytes])
|
||||
|
||||
// Some of these need special handling
|
||||
if k, ok := hexes[hex]; ok {
|
||||
return KeyMsg(k), nil
|
||||
}
|
||||
|
||||
// Get unicode value
|
||||
char, _ := utf8.DecodeRune(buf[:])
|
||||
if char == utf8.RuneError {
|
||||
return nil, errors.New("could not decode rune")
|
||||
}
|
||||
|
||||
// Is it a control character?
|
||||
if numBytes == 1 && char <= keyUS || char == keyDEL {
|
||||
return KeyMsg(Key{Type: KeyType(char)}), nil
|
||||
}
|
||||
|
||||
// Is it a special sequence, like an arrow key?
|
||||
if k, ok := sequences[string(buf[:numBytes])]; ok {
|
||||
return KeyMsg(Key{Type: k}), nil
|
||||
}
|
||||
|
||||
// Some of these need special handling
|
||||
hex := fmt.Sprintf("%x", buf[:numBytes])
|
||||
if k, ok := hexes[hex]; ok {
|
||||
return KeyMsg(k), nil
|
||||
}
|
||||
|
||||
var runes []rune
|
||||
b := buf[:numBytes]
|
||||
|
||||
// Translate input into runes. In most cases we'll receive exactly one
|
||||
// rune, but there are cases, particularly when an input method editor is
|
||||
// used, where we can receive multiple runes at once.
|
||||
for i, w := 0, 0; i < len(b); i += w {
|
||||
r, width := utf8.DecodeRune(b[i:])
|
||||
if r == utf8.RuneError {
|
||||
return nil, errors.New("could not decode rune")
|
||||
}
|
||||
runes = append(runes, r)
|
||||
w = width
|
||||
}
|
||||
|
||||
if len(runes) == 0 {
|
||||
return nil, errors.New("receied 0 runes from input")
|
||||
} else if len(runes) > 1 {
|
||||
// We received multiple runes, so we know this isn't a control
|
||||
// character, sequence, and so on.
|
||||
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
|
||||
}
|
||||
|
||||
// Is it a control character?
|
||||
char := runes[0]
|
||||
if numBytes == 1 && char <= keyUS || char == keyDEL {
|
||||
return KeyMsg(Key{Type: KeyType(char)}), nil
|
||||
}
|
||||
|
||||
// Is the alt key pressed? The buffer will be prefixed with an escape
|
||||
// sequence if so
|
||||
// sequence if so.
|
||||
if numBytes > 1 && buf[0] == 0x1b {
|
||||
// Now remove the initial escape sequence and re-process to get the
|
||||
// character.
|
||||
@@ -327,9 +349,9 @@ func readInput(r io.Reader) (Msg, error) {
|
||||
if c == utf8.RuneError {
|
||||
return nil, errors.New("could not decode rune after removing initial escape")
|
||||
}
|
||||
return KeyMsg(Key{Alt: true, Type: KeyRune, Rune: c}), nil
|
||||
return KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: runes}), nil
|
||||
}
|
||||
|
||||
// Just a regular, ol' rune
|
||||
return KeyMsg(Key{Type: KeyRune, Rune: char}), nil
|
||||
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user