From 9e8879939122261971d04c7fa9e93822e1246a1a Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 31 Jul 2025 13:31:14 -0500 Subject: [PATCH] KeyMap Expansion --- chat.go | 18 ++++++++-------- cli.go | 18 ++++++++-------- field.go | 16 +++++++------- keymap.go | 62 ++++++++++++++++++++++++++++++++++++++++++++--------- list.go | 22 +++++++++++++++---- menu.go | 13 +++++------ searcher.go | 16 +++++++------- 7 files changed, 111 insertions(+), 54 deletions(-) diff --git a/chat.go b/chat.go index 0f4cadc..c9da3b8 100644 --- a/chat.go +++ b/chat.go @@ -166,12 +166,12 @@ func (w *Chat) WantW() int { return w.w } func (w *Chat) WantH() int { return w.h } func (w *Chat) initKeyMap() { - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ - tcell.KeyEsc: func() bool { + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ + tcell.KeyEsc: func(ev *tcell.EventKey) bool { w.SetActive(false) return true }, - tcell.KeyUp: func() bool { + tcell.KeyUp: func(ev *tcell.EventKey) bool { if w.historyPosition < len(w.history)-1 { w.historyPosition++ w.value = w.history[w.historyPosition].Message @@ -180,7 +180,7 @@ func (w *Chat) initKeyMap() { } return false }, - tcell.KeyDown: func() bool { + tcell.KeyDown: func(ev *tcell.EventKey) bool { if w.historyPosition > -1 { w.historyPosition-- if w.historyPosition == -1 { @@ -193,31 +193,31 @@ func (w *Chat) initKeyMap() { } return false }, - tcell.KeyLeft: func() bool { + tcell.KeyLeft: func(ev *tcell.EventKey) bool { if w.cursor > 0 { w.cursor-- return true } return false }, - tcell.KeyRight: func() bool { + tcell.KeyRight: func(ev *tcell.EventKey) bool { if w.cursor < len(w.value) { w.cursor++ return true } return false }, - tcell.KeyCtrlU: func() bool { + tcell.KeyCtrlU: func(ev *tcell.EventKey) bool { w.value = w.value[w.cursor:] w.cursor = 0 return true }, - tcell.KeyTab: func() bool { + tcell.KeyTab: func(ev *tcell.EventKey) bool { // Auto-complete // TODO: Find best guess for current word return false }, - tcell.KeyEnter: func() bool { + tcell.KeyEnter: func(ev *tcell.EventKey) bool { // TODO: Submit the message return false }, diff --git a/cli.go b/cli.go index 0f3da79..d9cb9b7 100644 --- a/cli.go +++ b/cli.go @@ -171,12 +171,12 @@ func (w *Cli) WantW() int { return w.w } func (w *Cli) WantH() int { return w.h } func (w *Cli) initKeyMap() { - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ - tcell.KeyEsc: func() bool { + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ + tcell.KeyEsc: func(ev *tcell.EventKey) bool { w.SetActive(false) return true }, - tcell.KeyUp: func() bool { + tcell.KeyUp: func(ev *tcell.EventKey) bool { if w.historyPosition < len(w.history)-1 { w.historyPosition++ w.value = w.history[w.historyPosition] @@ -185,7 +185,7 @@ func (w *Cli) initKeyMap() { } return false }, - tcell.KeyDown: func() bool { + tcell.KeyDown: func(ev *tcell.EventKey) bool { if w.historyPosition > -1 { w.historyPosition-- if w.historyPosition == -1 { @@ -198,26 +198,26 @@ func (w *Cli) initKeyMap() { } return false }, - tcell.KeyLeft: func() bool { + tcell.KeyLeft: func(ev *tcell.EventKey) bool { if w.cursor > 0 { w.cursor-- return true } return false }, - tcell.KeyRight: func() bool { + tcell.KeyRight: func(ev *tcell.EventKey) bool { if w.cursor < len(w.value) { w.cursor++ return true } return false }, - tcell.KeyCtrlU: func() bool { + tcell.KeyCtrlU: func(ev *tcell.EventKey) bool { w.value = w.value[w.cursor:] w.cursor = 0 return true }, - tcell.KeyTab: func() bool { + tcell.KeyTab: func(ev *tcell.EventKey) bool { // Auto-complete guess := w.findBestGuess() if guess != "" { @@ -227,7 +227,7 @@ func (w *Cli) initKeyMap() { } return false }, - tcell.KeyEnter: func() bool { + tcell.KeyEnter: func(ev *tcell.EventKey) bool { cmds := strings.Split(w.value, " ") if v, ok := w.commandMap[cmds[0]]; ok { w.history = append(w.history, w.value) diff --git a/field.go b/field.go index 345eb4b..7958c93 100644 --- a/field.go +++ b/field.go @@ -63,7 +63,7 @@ func (w *Field) Init(id string, style tcell.Style) { return h.IsBS(*ev) || h.KeyIsDisplayable(*ev) } - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ tcell.KeyLeft: w.handleLeft, tcell.KeyRight: w.handleRight, tcell.KeyHome: w.handleHome, @@ -79,7 +79,7 @@ func (w *Field) HandleKey(ev *tcell.EventKey) bool { return false } if h.IsBS(*ev) { - return w.handleBackspace() + return w.handleBackspace(ev) } if ok := w.keyMap.Handle(ev); ok { return true @@ -153,7 +153,7 @@ func (w *Field) SetSize(c Coord) { w.w, w.h = c.X, c.Y } func (w *Field) Focusable() bool { return true } /* Non-Widget-Interface Functions */ -func (w *Field) handleBackspace() bool { +func (w *Field) handleBackspace(ev *tcell.EventKey) bool { st := w.cursor if w.cursor > 0 { w.cursor-- @@ -167,7 +167,7 @@ func (w *Field) handleBackspace() bool { return false } -func (w *Field) handleLeft() bool { +func (w *Field) handleLeft(ev *tcell.EventKey) bool { if w.cursor > 0 { w.cursor-- return true @@ -175,7 +175,7 @@ func (w *Field) handleLeft() bool { return false } -func (w *Field) handleRight() bool { +func (w *Field) handleRight(ev *tcell.EventKey) bool { if w.cursor < len(w.value) { w.cursor++ return true @@ -183,18 +183,18 @@ func (w *Field) handleRight() bool { return false } -func (w *Field) clearValueBeforeCursor() bool { +func (w *Field) clearValueBeforeCursor(ev *tcell.EventKey) bool { w.SetValue(w.value[w.cursor:]) w.cursor = 0 return true } -func (w *Field) handleHome() bool { +func (w *Field) handleHome(ev *tcell.EventKey) bool { w.cursor = 0 return true } -func (w *Field) handleEnd() bool { +func (w *Field) handleEnd(ev *tcell.EventKey) bool { w.cursor = len(w.value) return true } diff --git a/keymap.go b/keymap.go index 0f75cb2..3946fce 100644 --- a/keymap.go +++ b/keymap.go @@ -23,27 +23,69 @@ package widgets import "github.com/gdamore/tcell" -type KeyMap map[tcell.Key]func() bool +type KeyMap struct { + Keys map[tcell.Key]func(*tcell.EventKey) bool + Runes map[rune]func(*tcell.EventKey) bool +} func BlankKeyMap() KeyMap { - return KeyMap(make(map[tcell.Key]func() bool)) + return KeyMap{ + Keys: make(map[tcell.Key]func(*tcell.EventKey) bool), + Runes: make(map[rune]func(*tcell.EventKey) bool), + } } -func NewKeyMap(m map[tcell.Key]func() bool) KeyMap { - return KeyMap(m) +func NewKeyMap(m map[tcell.Key]func(*tcell.EventKey) bool) KeyMap { + return KeyMap{ + Keys: m, + Runes: make(map[rune]func(*tcell.EventKey) bool), + } } -func (m KeyMap) Add(k tcell.Key, do func() bool) { m[k] = do } +func NewRuneMap(m map[rune]func(*tcell.EventKey) bool) KeyMap { + return KeyMap{ + Keys: make(map[tcell.Key]func(*tcell.EventKey) bool), + Runes: m, + } +} + +func (m KeyMap) Add(k tcell.Key, do func(*tcell.EventKey) bool) { m.Keys[k] = do } func (m KeyMap) Remove(k tcell.Key) { - if _, ok := m[k]; ok { - delete(m, k) + if _, ok := m.Keys[k]; ok { + delete(m.Keys, k) + } +} + +func (m KeyMap) AddAll(all map[tcell.Key]func(*tcell.EventKey) bool) { + for k, v := range all { + m.Add(k, v) + } +} +func (m KeyMap) AddRune(k rune, do func(*tcell.EventKey) bool) { m.Runes[k] = do } +func (m KeyMap) RemoveRune(k rune) { + if _, ok := m.Runes[k]; ok { + delete(m.Runes, k) + } +} + +func (m KeyMap) AddRunes(all map[rune]func(*tcell.EventKey) bool) { + for k, v := range all { + m.AddRune(k, v) } } func (m KeyMap) Handle(ev *tcell.EventKey) bool { - for k, v := range m { - if ev.Key() == k { - return v() + if ev.Key() == tcell.KeyRune { + for k, v := range m.Runes { + if ev.Rune() == k { + return v(ev) + } + } + } else { + for k, v := range m.Keys { + if ev.Key() == k { + return v(ev) + } } } return false diff --git a/list.go b/list.go index 243cfed..b6a97d6 100644 --- a/list.go +++ b/list.go @@ -46,6 +46,7 @@ type List struct { onSelect func(int, string) bool keyMap KeyMap + vimMode bool } var _ Widget = (*List)(nil) @@ -59,16 +60,28 @@ func NewList(id string, style tcell.Style) *List { func (w *List) Init(id string, style tcell.Style) { w.id = id w.style = style - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ tcell.KeyUp: w.MoveUp, tcell.KeyDown: w.MoveDown, - tcell.KeyEnter: func() bool { + tcell.KeyEnter: func(ev *tcell.EventKey) bool { if w.onSelect != nil && w.cursor < len(w.list) { return w.onSelect(w.cursor, w.list[w.cursor]) } return false }, }) + w.keyMap.AddRune('j', func(ev *tcell.EventKey) bool { + if w.vimMode { + return w.MoveDown(ev) + } + return false + }) + w.keyMap.AddRune('k', func(ev *tcell.EventKey) bool { + if w.vimMode { + return w.MoveUp(ev) + } + return false + }) w.itemsStyle = make(map[int]tcell.Style) } func (w *List) Id() string { return w.id } @@ -149,7 +162,7 @@ func (w *List) WantH() int { func (w *List) SetFocusable(f bool) { w.focusable = f } func (w *List) SetCursorWrap(b bool) { w.cursorWrap = b } -func (w *List) MoveUp() bool { +func (w *List) MoveUp(ev *tcell.EventKey) bool { if w.cursor > 0 { w.cursor-- return true @@ -160,7 +173,7 @@ func (w *List) MoveUp() bool { return false } -func (w *List) MoveDown() bool { +func (w *List) MoveDown(ev *tcell.EventKey) bool { if w.cursor < len(w.list)-1 { w.cursor++ return true @@ -204,3 +217,4 @@ func (w *List) SetItem(idx int, txt string) { func (w *List) GetIdx() int { return w.cursor } func (w *List) ClearBorder() { w.border = []rune{} } func (w *List) SetOnSelect(s func(int, string) bool) { w.onSelect = s } +func (w *List) SetVimMode(b bool) { w.vimMode = b } diff --git a/menu.go b/menu.go index 90ae7e4..5551176 100644 --- a/menu.go +++ b/menu.go @@ -41,6 +41,7 @@ type Menu struct { onPressed func() bool manualExpand bool expanded bool + vimMode bool keyMap KeyMap } @@ -62,12 +63,12 @@ func (w *Menu) Init(id string, style tcell.Style) { w.id = id w.style = style w.visible = true - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ tcell.KeyRight: w.MoveRight, tcell.KeyLeft: w.MoveLeft, tcell.KeyUp: w.MoveUp, tcell.KeyDown: w.MoveDown, - tcell.KeyEnter: func() bool { + tcell.KeyEnter: func(ev *tcell.EventKey) bool { if w.onPressed != nil { return w.onPressed() } @@ -201,7 +202,7 @@ func (w *Menu) AddItems(iL ...Widget) { w.SetW(maxW) } -func (w *Menu) MoveRight() bool { +func (w *Menu) MoveRight(ev *tcell.EventKey) bool { if w.menuType != MenuTypeH { return false } @@ -209,7 +210,7 @@ func (w *Menu) MoveRight() bool { return true } -func (w *Menu) MoveLeft() bool { +func (w *Menu) MoveLeft(ev *tcell.EventKey) bool { if w.menuType != MenuTypeH { return false } @@ -217,7 +218,7 @@ func (w *Menu) MoveLeft() bool { return true } -func (w *Menu) MoveUp() bool { +func (w *Menu) MoveUp(ev *tcell.EventKey) bool { if w.menuType != MenuTypeV { return false } @@ -225,7 +226,7 @@ func (w *Menu) MoveUp() bool { return true } -func (w *Menu) MoveDown() bool { +func (w *Menu) MoveDown(ev *tcell.EventKey) bool { if w.menuType != MenuTypeV { return false } diff --git a/searcher.go b/searcher.go index 2506db5..64a8672 100644 --- a/searcher.go +++ b/searcher.go @@ -68,7 +68,7 @@ func (w *Searcher) Init(id string, style tcell.Style) { w.search.SetOnChange(func(prev, curr string) { w.updateFilter() }) - w.keyMap = NewKeyMap(map[tcell.Key]func() bool{ + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ tcell.KeyUp: w.handleKeyUp, tcell.KeyDown: w.handleKeyDown, tcell.KeyHome: w.handleKeyHome, @@ -90,17 +90,17 @@ func (w *Searcher) HandleKey(ev *tcell.EventKey) bool { return w.search.HandleKey(ev) } -func (w *Searcher) handleKeyUp() bool { +func (w *Searcher) handleKeyUp(ev *tcell.EventKey) bool { w.cursor = ((w.cursor - 1) + len(w.filteredData)) % len(w.filteredData) return true } -func (w *Searcher) handleKeyDown() bool { +func (w *Searcher) handleKeyDown(ev *tcell.EventKey) bool { w.cursor = ((w.cursor + 1) + len(w.filteredData)) % len(w.filteredData) return true } -func (w *Searcher) handleKeyHome() bool { +func (w *Searcher) handleKeyHome(ev *tcell.EventKey) bool { if w.cursor == 0 { return false } @@ -108,7 +108,7 @@ func (w *Searcher) handleKeyHome() bool { return true } -func (w *Searcher) handleKeyEnd() bool { +func (w *Searcher) handleKeyEnd(ev *tcell.EventKey) bool { if w.cursor == len(w.filteredData)-1 { return false } @@ -116,7 +116,7 @@ func (w *Searcher) handleKeyEnd() bool { return true } -func (w *Searcher) handleKeyPgUp() bool { +func (w *Searcher) handleKeyPgUp(ev *tcell.EventKey) bool { if w.cursor == 0 { return false } @@ -128,7 +128,7 @@ func (w *Searcher) handleKeyPgUp() bool { return false } -func (w *Searcher) handleKeyPgDn() bool { +func (w *Searcher) handleKeyPgDn(ev *tcell.EventKey) bool { mx := len(w.filteredData) - 1 if w.cursor == mx { return false @@ -140,7 +140,7 @@ func (w *Searcher) handleKeyPgDn() bool { return false } -func (w *Searcher) handleKeyEnter() bool { +func (w *Searcher) handleKeyEnter(ev *tcell.EventKey) bool { if w.hideOnSelect { w.visible = false }