Configurable keybindings

This commit is contained in:
3nprob 2021-12-07 22:18:59 +09:00
parent 4e8c7b6759
commit 5e3ccb8c53
6 changed files with 163 additions and 52 deletions

View File

@ -29,6 +29,9 @@ import (
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/pushrules" "maunium.net/go/mautrix/pushrules"
"github.com/3nprob/cbind"
"maunium.net/go/tcell"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
) )
@ -56,6 +59,26 @@ type UserPreferences struct {
AltEnterToSend bool `yaml:"alt_enter_to_send"` AltEnterToSend bool `yaml:"alt_enter_to_send"`
} }
type Keybind struct {
Mod tcell.ModMask
Key tcell.Key
Ch rune
}
type Keybindings struct {
Main map[Keybind]string `yaml:"main,omitempty"`
Room map[Keybind]string `yaml:"room,omitempty"`
Modal map[Keybind]string `yaml:"modal,omitempty"`
Visual map[Keybind]string `yaml:"visual,omitempty"`
}
type KeybindingsConfig struct {
Main map[string]string `yaml:"main,omitempty"`
Room map[string]string `yaml:"room,omitempty"`
Modal map[string]string `yaml:"modal,omitempty"`
Visual map[string]string `yaml:"visual,omitempty"`
}
// Config contains the main config of gomuks. // Config contains the main config of gomuks.
type Config struct { type Config struct {
UserID id.UserID `yaml:"mxid"` UserID id.UserID `yaml:"mxid"`
@ -85,6 +108,7 @@ type Config struct {
AuthCache AuthCache `yaml:"-"` AuthCache AuthCache `yaml:"-"`
Rooms *rooms.RoomCache `yaml:"-"` Rooms *rooms.RoomCache `yaml:"-"`
PushRules *pushrules.PushRuleset `yaml:"-"` PushRules *pushrules.PushRuleset `yaml:"-"`
Keybindings Keybindings `yaml:"-"`
nosave bool nosave bool
} }
@ -152,6 +176,7 @@ func (config *Config) LoadAll() {
config.LoadAuthCache() config.LoadAuthCache()
config.LoadPushRules() config.LoadPushRules()
config.LoadPreferences() config.LoadPreferences()
config.LoadKeybindings()
err := config.Rooms.LoadList() err := config.Rooms.LoadList()
if err != nil { if err != nil {
panic(err) panic(err)
@ -189,6 +214,70 @@ func (config *Config) SavePreferences() {
config.save("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences) config.save("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences)
} }
func (config *Config) LoadKeybindings() {
cfg := KeybindingsConfig{}
config.load("keybindings", config.Dir, "keybindings.yaml", &cfg)
config.Keybindings.Main = make(map[Keybind]string)
config.Keybindings.Room = make(map[Keybind]string)
config.Keybindings.Modal = make(map[Keybind]string)
config.Keybindings.Visual = make(map[Keybind]string)
for k, v := range cfg.Main {
mod, key, ch, err := cbind.Decode(k)
if err != nil {
// todo
}
kb := Keybind{
Mod: mod,
Key: key,
Ch: ch,
}
config.Keybindings.Main[kb] = v
}
for k, v := range cfg.Room {
mod, key, ch, err := cbind.Decode(k)
if err != nil {
// todo
}
kb := Keybind{
Mod: mod,
Key: key,
Ch: ch,
}
config.Keybindings.Room[kb] = v
}
for k, v := range cfg.Modal {
mod, key, ch, err := cbind.Decode(k)
if err != nil {
// todo
}
kb := Keybind{
Mod: mod,
Key: key,
Ch: ch,
}
config.Keybindings.Modal[kb] = v
}
for k, v := range cfg.Visual {
mod, key, ch, err := cbind.Decode(k)
if err != nil {
// todo
}
kb := Keybind{
Mod: mod,
Key: key,
Ch: ch,
}
config.Keybindings.Visual[kb] = v
}
}
func (config *Config) SaveKeybindings() {
config.save("keybindings", config.Dir, "keybindings.yaml", &config.Keybindings)
}
func (config *Config) LoadAuthCache() { func (config *Config) LoadAuthCache() {
config.load("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache) config.load("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache)
} }

View File

@ -27,6 +27,7 @@ import (
"maunium.net/go/mauview" "maunium.net/go/mauview"
"maunium.net/go/tcell" "maunium.net/go/tcell"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
) )
@ -120,12 +121,17 @@ func (fs *FuzzySearchModal) changeHandler(str string) {
func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool { func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
highlights := fs.results.GetHighlights() highlights := fs.results.GetHighlights()
switch event.Key() { kb := config.Keybind{
case tcell.KeyEsc: Key: event.Key(),
Ch: event.Rune(),
Mod: event.Modifiers(),
}
switch fs.parent.config.Keybindings.Modal[kb] {
case "cancel":
// Close room finder // Close room finder
fs.parent.HideModal() fs.parent.HideModal()
return true return true
case tcell.KeyTab: case "select_next":
// Cycle highlighted area to next match // Cycle highlighted area to next match
if len(highlights) > 0 { if len(highlights) > 0 {
fs.selected = (fs.selected + 1) % len(fs.matches) fs.selected = (fs.selected + 1) % len(fs.matches)
@ -133,7 +139,7 @@ func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
fs.results.ScrollToHighlight() fs.results.ScrollToHighlight()
} }
return true return true
case tcell.KeyBacktab: case "select_prev":
if len(highlights) > 0 { if len(highlights) > 0 {
fs.selected = (fs.selected - 1) % len(fs.matches) fs.selected = (fs.selected - 1) % len(fs.matches)
if fs.selected < 0 { if fs.selected < 0 {
@ -143,7 +149,7 @@ func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
fs.results.ScrollToHighlight() fs.results.ScrollToHighlight()
} }
return true return true
case tcell.KeyEnter: case "confirm":
// Switch room to currently selected room // Switch room to currently selected room
if len(highlights) > 0 { if len(highlights) > 0 {
debug.Print("Fuzzy Selected Room:", fs.roomList[fs.matches[fs.selected].OriginalIndex].GetTitle()) debug.Print("Fuzzy Selected Room:", fs.roomList[fs.matches[fs.selected].OriginalIndex].GetTitle())

View File

@ -1,6 +1,7 @@
package ui package ui
import ( import (
"maunium.net/go/gomuks/config"
"maunium.net/go/tcell" "maunium.net/go/tcell"
"maunium.net/go/mauview" "maunium.net/go/mauview"
@ -94,7 +95,12 @@ func NewHelpModal(parent *MainView) *HelpModal {
} }
func (hm *HelpModal) OnKeyEvent(event mauview.KeyEvent) bool { func (hm *HelpModal) OnKeyEvent(event mauview.KeyEvent) bool {
if event.Key() == tcell.KeyEscape || event.Rune() == 'q' { kb := config.Keybind{
Key: event.Key(),
Ch: event.Rune(),
Mod: event.Modifiers(),
}
if hm.parent.config.Keybindings.Room[kb] == "cancel" {
hm.parent.HideModal() hm.parent.HideModal()
return true return true
} }

View File

@ -37,7 +37,7 @@ import (
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface" ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/lib/open" "maunium.net/go/gomuks/lib/open"
"maunium.net/go/gomuks/lib/util" "maunium.net/go/gomuks/lib/util"
"maunium.net/go/gomuks/matrix/muksevt" "maunium.net/go/gomuks/matrix/muksevt"
@ -339,42 +339,45 @@ func (view *RoomView) ClearAllContext() {
func (view *RoomView) OnKeyEvent(event mauview.KeyEvent) bool { func (view *RoomView) OnKeyEvent(event mauview.KeyEvent) bool {
msgView := view.MessageView() msgView := view.MessageView()
kb := config.Keybind{
Key: event.Key(),
Ch: event.Rune(),
Mod: event.Modifiers(),
}
if view.selecting { if view.selecting {
k := event.Key() switch view.config.Keybindings.Visual[kb] {
c := event.Rune() case "clear":
switch {
case k == tcell.KeyEscape || c == 'h':
view.ClearAllContext() view.ClearAllContext()
case k == tcell.KeyUp || c == 'k': case "select_prev":
view.SelectPrevious() view.SelectPrevious()
case k == tcell.KeyDown || c == 'j': case "select_next":
view.SelectNext() view.SelectNext()
case k == tcell.KeyEnter || c == 'l': case "confirm":
view.OnSelect(msgView.selected) view.OnSelect(msgView.selected)
default: default:
return false return false
} }
return true return true
} }
switch event.Key() {
case tcell.KeyEscape: switch view.config.Keybindings.Room[kb] {
case "clear":
view.ClearAllContext() view.ClearAllContext()
return true return true
case tcell.KeyPgUp: case "scroll_up":
if msgView.IsAtTop() { if msgView.IsAtTop() {
go view.parent.LoadHistory(view.Room.ID) go view.parent.LoadHistory(view.Room.ID)
} }
msgView.AddScrollOffset(+msgView.Height() / 2) msgView.AddScrollOffset(+msgView.Height() / 2)
return true return true
case tcell.KeyPgDn: case "scroll_down":
msgView.AddScrollOffset(-msgView.Height() / 2) msgView.AddScrollOffset(-msgView.Height() / 2)
return true return true
case tcell.KeyEnter: case "send":
if (event.Modifiers()&tcell.ModShift == 0 && event.Modifiers()&tcell.ModCtrl == 0) != (view.config.Preferences.AltEnterToSend) {
view.InputSubmit(view.input.GetText()) view.InputSubmit(view.input.GetText())
return true return true
} }
}
return view.input.OnKeyEvent(event) return view.input.OnKeyEvent(event)
} }

View File

@ -27,6 +27,7 @@ import (
"maunium.net/go/mauview" "maunium.net/go/mauview"
"maunium.net/go/tcell" "maunium.net/go/tcell"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/mautrix/crypto" "maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/event" "maunium.net/go/mautrix/event"
@ -207,8 +208,13 @@ func (vm *VerificationModal) OnSuccess() {
} }
func (vm *VerificationModal) OnKeyEvent(event mauview.KeyEvent) bool { func (vm *VerificationModal) OnKeyEvent(event mauview.KeyEvent) bool {
kb := config.Keybind{
Key: event.Key(),
Ch: event.Rune(),
Mod: event.Modifiers(),
}
if vm.done { if vm.done {
if event.Key() == tcell.KeyEnter || event.Key() == tcell.KeyEsc { if vm.parent.config.Keybindings.Modal[kb] == "cancel" || vm.parent.config.Keybindings.Modal[kb] == "confirm" {
vm.parent.HideModal() vm.parent.HideModal()
return true return true
} }
@ -217,7 +223,7 @@ func (vm *VerificationModal) OnKeyEvent(event mauview.KeyEvent) bool {
debug.Print("Ignoring pre-emoji key event") debug.Print("Ignoring pre-emoji key event")
return false return false
} }
if event.Key() == tcell.KeyEnter { if vm.parent.config.Keybindings.Modal[kb] == "confirm" {
text := strings.ToLower(strings.TrimSpace(vm.inputBar.GetText())) text := strings.ToLower(strings.TrimSpace(vm.inputBar.GetText()))
if text == "yes" { if text == "yes" {
debug.Print("Confirming verification") debug.Print("Confirming verification")

View File

@ -33,7 +33,7 @@ import (
"maunium.net/go/gomuks/config" "maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface" ifc "maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/lib/notification" "maunium.net/go/gomuks/lib/notification"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
"maunium.net/go/gomuks/ui/widget" "maunium.net/go/gomuks/ui/widget"
@ -170,33 +170,34 @@ func (view *MainView) OnKeyEvent(event mauview.KeyEvent) bool {
return view.modal.OnKeyEvent(event) return view.modal.OnKeyEvent(event)
} }
k := event.Key() kb := config.Keybind{
c := event.Rune() Key: event.Key(),
if event.Modifiers() == tcell.ModCtrl || event.Modifiers() == tcell.ModAlt { Ch: event.Rune(),
switch { Mod: event.Modifiers(),
case k == tcell.KeyDown: }
switch view.config.Keybindings.Main[kb] {
case "next_room":
view.SwitchRoom(view.roomList.Next()) view.SwitchRoom(view.roomList.Next())
case k == tcell.KeyUp: case "prev_room":
view.SwitchRoom(view.roomList.Previous()) view.SwitchRoom(view.roomList.Previous())
case c == 'k' || k == tcell.KeyCtrlK: case "search_rooms":
view.ShowModal(NewFuzzySearchModal(view, 42, 12)) view.ShowModal(NewFuzzySearchModal(view, 42, 12))
case k == tcell.KeyHome: case "scroll_up":
msgView := view.currentRoom.MessageView() msgView := view.currentRoom.MessageView()
msgView.AddScrollOffset(msgView.TotalHeight()) msgView.AddScrollOffset(msgView.TotalHeight())
case k == tcell.KeyEnd: case "scroll_down":
msgView := view.currentRoom.MessageView() msgView := view.currentRoom.MessageView()
msgView.AddScrollOffset(-msgView.TotalHeight()) msgView.AddScrollOffset(-msgView.TotalHeight())
case k == tcell.KeyEnter: case "add_newline":
return view.flex.OnKeyEvent(tcell.NewEventKey(tcell.KeyEnter, '\n', event.Modifiers()|tcell.ModShift, "")) return view.flex.OnKeyEvent(tcell.NewEventKey(tcell.KeyEnter, '\n', event.Modifiers()|tcell.ModShift, ""))
case c == 'a': case "next_active_room":
view.SwitchRoom(view.roomList.NextWithActivity()) view.SwitchRoom(view.roomList.NextWithActivity())
case c == 'l' || k == tcell.KeyCtrlL: case "show_bare":
view.ShowBare(view.currentRoom) view.ShowBare(view.currentRoom)
default: default:
goto defaultHandler goto defaultHandler
} }
return true return true
}
defaultHandler: defaultHandler:
if view.config.Preferences.HideRoomList { if view.config.Preferences.HideRoomList {
return view.roomView.OnKeyEvent(event) return view.roomView.OnKeyEvent(event)