Make keys mappable
This commit is contained in:
parent
e9d0ab8162
commit
ad948508f4
47
README.md
47
README.md
@ -35,27 +35,30 @@ Getting started
|
||||
|
||||
// optional: define custom key mappings
|
||||
// (shown are the default key mappings)
|
||||
"keys": {
|
||||
"normal": {
|
||||
"i": "insert",
|
||||
"k": "channel-up",
|
||||
"j": "channel-down",
|
||||
"gg": "channel-top",
|
||||
"G": "channel-bottom",
|
||||
"pg-up": "chat-up",
|
||||
"ctrl-b": "chat-up",
|
||||
"ctrl-u": "chat-up",
|
||||
"pg-down": "chat-down",
|
||||
"ctrl-f": "chat-down",
|
||||
"ctrl-d": "chat-down",
|
||||
"q": "quit"
|
||||
},
|
||||
"insert": {
|
||||
"left": "cursor-left",
|
||||
"right": "cursor-right",
|
||||
"enter": "send",
|
||||
"esc": "normal"
|
||||
}
|
||||
"key-map": {
|
||||
"command": {
|
||||
"i": "mode-insert",
|
||||
"k": "channel-up",
|
||||
"j": "channel-down",
|
||||
"g": "channel-top",
|
||||
"G": "channel-bottom",
|
||||
"<previous>": "chat-up",
|
||||
"C-b": "chat-up",
|
||||
"C-u": "chat-up",
|
||||
"<next>": "chat-down",
|
||||
"C-f": "chat-down",
|
||||
"C-d": "chat-down",
|
||||
"q": "quit",
|
||||
},
|
||||
"insert": {
|
||||
"<left>": "cursor-left",
|
||||
"<right>": "cursor-right",
|
||||
"<enter>": "send",
|
||||
"<escape>": "mode-command",
|
||||
"<backspace>": "backspace",
|
||||
"<delete>": "delete",
|
||||
"<space>": "space",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -77,7 +80,7 @@ Default Key Mapping
|
||||
| normal | `i` | insert mode |
|
||||
| normal | `k` | move channel cursor up |
|
||||
| normal | `j` | move channel cursor down |
|
||||
| normal | `gg` | move channel cursor top |
|
||||
| normal | `g` | move channel cursor top |
|
||||
| normal | `G` | move channel cursor bottom |
|
||||
| normal | `pg-up` | scroll chat pane up |
|
||||
| normal | `ctrl-b` | scroll chat pane up |
|
||||
|
@ -14,7 +14,7 @@ type Config struct {
|
||||
Theme string `json:"theme"`
|
||||
SidebarWidth int `json:"sidebar_width"`
|
||||
MainWidth int `json:"-"`
|
||||
KeyMapping map[string]keyMapping `json:"keys"`
|
||||
KeyMap map[string]keyMapping `json:"key-map"`
|
||||
}
|
||||
|
||||
type keyMapping map[string]string
|
||||
@ -25,28 +25,29 @@ func NewConfig(filepath string) (*Config, error) {
|
||||
Theme: "dark",
|
||||
SidebarWidth: 1,
|
||||
MainWidth: 11,
|
||||
KeyMapping: map[string]keyMapping{
|
||||
"normal": keyMapping{
|
||||
"i": "insert",
|
||||
"k": "channel-up",
|
||||
"j": "channel-down",
|
||||
"g": "channel-top",
|
||||
"G": "channel-bottom",
|
||||
"pg-up": "chat-up",
|
||||
"ctrl-b": "chat-up",
|
||||
"ctrl-u": "chat-up",
|
||||
"pg-down": "chat-down",
|
||||
"ctrl-f": "chat-down",
|
||||
"ctrl-d": "chat-down",
|
||||
"q": "quit",
|
||||
KeyMap: map[string]keyMapping{
|
||||
"command": {
|
||||
"i": "mode-insert",
|
||||
"k": "channel-up",
|
||||
"j": "channel-down",
|
||||
"g": "channel-top",
|
||||
"G": "channel-bottom",
|
||||
"<previous>": "chat-up",
|
||||
"C-b": "chat-up",
|
||||
"C-u": "chat-up",
|
||||
"<next>": "chat-down",
|
||||
"C-f": "chat-down",
|
||||
"C-d": "chat-down",
|
||||
"q": "quit",
|
||||
},
|
||||
"insert": keyMapping{
|
||||
"left": "cursor-left",
|
||||
"right": "cursor-right",
|
||||
"enter": "send",
|
||||
"esc": "normal",
|
||||
"backspace": "backspace",
|
||||
"del": "delete",
|
||||
"insert": {
|
||||
"<left>": "cursor-left",
|
||||
"<right>": "cursor-right",
|
||||
"<enter>": "send",
|
||||
"<escape>": "mode-command",
|
||||
"<backspace>": "backspace",
|
||||
"<delete>": "delete",
|
||||
"<space>": "space",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
CommandMode = "normal"
|
||||
CommandMode = "command"
|
||||
InsertMode = "insert"
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gizak/termui"
|
||||
"github.com/nlopes/slack"
|
||||
@ -11,37 +11,19 @@ import (
|
||||
"github.com/erroneousboat/slack-term/views"
|
||||
)
|
||||
|
||||
func RegisterEventHandlers(ctx *context.AppContext) {
|
||||
anyKeyHandler(ctx)
|
||||
incomingMessageHandler(ctx)
|
||||
termui.Handle("/sys/wnd/resize", resizeHandler(ctx))
|
||||
}
|
||||
|
||||
var keyMapping = map[termbox.Key]string{
|
||||
termbox.KeyPgup: "pg-up",
|
||||
termbox.KeyCtrlB: "ctrl-b",
|
||||
termbox.KeyCtrlU: "ctrl-u",
|
||||
termbox.KeyPgdn: "pg-dn",
|
||||
termbox.KeyCtrlF: "ctrl-f",
|
||||
termbox.KeyCtrlD: "ctrl-d",
|
||||
termbox.KeyEsc: "esc",
|
||||
termbox.KeyEnter: "enter",
|
||||
termbox.KeyBackspace: "backspace",
|
||||
termbox.KeyBackspace2: "backspace",
|
||||
termbox.KeyDelete: "del",
|
||||
termbox.KeyArrowRight: "right",
|
||||
termbox.KeyArrowLeft: "left",
|
||||
}
|
||||
|
||||
// actionMap binds specific action names to the function counterparts,
|
||||
// these action names can then be used to bind them to specific keys
|
||||
// in the Config.
|
||||
var actionMap = map[string]func(*context.AppContext){
|
||||
"space": actionSpace,
|
||||
"backspace": actionBackSpace,
|
||||
"delete": actionDelete,
|
||||
"cursor-right": actionMoveCursorRight,
|
||||
"cursor-left": actionMoveCursorLeft,
|
||||
"send": actionSend,
|
||||
"quit": actionQuit,
|
||||
"insert": actionInsertMode,
|
||||
"normal": actionCommandMode,
|
||||
"mode-insert": actionInsertMode,
|
||||
"mode-command": actionCommandMode,
|
||||
"channel-up": actionMoveCursorUpChannels,
|
||||
"channel-down": actionMoveCursorDownChannels,
|
||||
"channel-top": actionMoveCursorTopChannels,
|
||||
@ -50,6 +32,12 @@ var actionMap = map[string]func(*context.AppContext){
|
||||
"chat-down": actionScrollDownChat,
|
||||
}
|
||||
|
||||
func RegisterEventHandlers(ctx *context.AppContext) {
|
||||
anyKeyHandler(ctx)
|
||||
incomingMessageHandler(ctx)
|
||||
termui.Handle("/sys/wnd/resize", resizeHandler(ctx))
|
||||
}
|
||||
|
||||
func anyKeyHandler(ctx *context.AppContext) {
|
||||
go func() {
|
||||
for {
|
||||
@ -59,23 +47,20 @@ func anyKeyHandler(ctx *context.AppContext) {
|
||||
continue
|
||||
}
|
||||
|
||||
mappedKey := keyMapping[ev.Key]
|
||||
if mappedKey == "" {
|
||||
mappedKey = fmt.Sprintf("%c", ev.Ch)
|
||||
}
|
||||
keyStr := getKeyString(ev)
|
||||
|
||||
mappedActionName := ctx.Config.KeyMapping[ctx.Mode][mappedKey]
|
||||
action := actionMap[mappedActionName]
|
||||
if action != nil {
|
||||
action(ctx)
|
||||
continue
|
||||
}
|
||||
|
||||
if ctx.Mode == context.InsertMode {
|
||||
switch ev.Key {
|
||||
case termbox.KeySpace:
|
||||
actionInput(ctx.View, ' ')
|
||||
default:
|
||||
// Get the action name (actionStr) from the key that
|
||||
// has been pressed. If this is found try to uncover
|
||||
// the associated function with this key and execute
|
||||
// it.
|
||||
actionStr, ok := ctx.Config.KeyMap[ctx.Mode][keyStr]
|
||||
if ok {
|
||||
action, ok := actionMap[actionStr]
|
||||
if ok {
|
||||
action(ctx)
|
||||
}
|
||||
} else {
|
||||
if ctx.Mode == context.InsertMode && ev.Ch != 0 {
|
||||
actionInput(ctx.View, ev.Ch)
|
||||
}
|
||||
}
|
||||
@ -142,6 +127,10 @@ func actionInput(view *views.View, key rune) {
|
||||
termui.Render(view.Input)
|
||||
}
|
||||
|
||||
func actionSpace(ctx *context.AppContext) {
|
||||
actionInput(ctx.View, ' ')
|
||||
}
|
||||
|
||||
func actionBackSpace(ctx *context.AppContext) {
|
||||
ctx.View.Input.Backspace()
|
||||
termui.Render(ctx.View.Input)
|
||||
@ -262,3 +251,53 @@ func actionScrollDownChat(ctx *context.AppContext) {
|
||||
ctx.View.Chat.ScrollDown()
|
||||
termui.Render(ctx.View.Chat)
|
||||
}
|
||||
|
||||
// GetKeyString will return a string that resembles the key event from
|
||||
// termbox. This is blatanly copied from termui because it is an unexported
|
||||
// function.
|
||||
//
|
||||
// See:
|
||||
// - https://github.com/gizak/termui/blob/a7e3aeef4cdf9fa2edb723b1541cb69b7bb089ea/events.go#L31-L72
|
||||
// - https://github.com/nsf/termbox-go/blob/master/api_common.go
|
||||
func getKeyString(e termbox.Event) string {
|
||||
var ek string
|
||||
|
||||
k := string(e.Ch)
|
||||
pre := ""
|
||||
mod := ""
|
||||
|
||||
if e.Mod == termbox.ModAlt {
|
||||
mod = "M-"
|
||||
}
|
||||
if e.Ch == 0 {
|
||||
if e.Key > 0xFFFF-12 {
|
||||
k = "<f" + strconv.Itoa(0xFFFF-int(e.Key)+1) + ">"
|
||||
} else if e.Key > 0xFFFF-25 {
|
||||
ks := []string{"<insert>", "<delete>", "<home>", "<end>", "<previous>", "<next>", "<up>", "<down>", "<left>", "<right>"}
|
||||
k = ks[0xFFFF-int(e.Key)-12]
|
||||
}
|
||||
|
||||
if e.Key <= 0x7F {
|
||||
pre = "C-"
|
||||
k = string('a' - 1 + int(e.Key))
|
||||
kmap := map[termbox.Key][2]string{
|
||||
termbox.KeyCtrlSpace: {"C-", "<space>"},
|
||||
termbox.KeyBackspace: {"", "<backspace>"},
|
||||
termbox.KeyTab: {"", "<tab>"},
|
||||
termbox.KeyEnter: {"", "<enter>"},
|
||||
termbox.KeyEsc: {"", "<escape>"},
|
||||
termbox.KeyCtrlBackslash: {"C-", "\\"},
|
||||
termbox.KeyCtrlSlash: {"C-", "/"},
|
||||
termbox.KeySpace: {"", "<space>"},
|
||||
termbox.KeyCtrl8: {"C-", "8"},
|
||||
}
|
||||
if sk, ok := kmap[e.Key]; ok {
|
||||
pre = sk[0]
|
||||
k = sk[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ek = pre + mod + k
|
||||
return ek
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user