Configurable keybindings
This commit is contained in:
		@@ -29,6 +29,9 @@ import (
 | 
			
		||||
	"maunium.net/go/mautrix/id"
 | 
			
		||||
	"maunium.net/go/mautrix/pushrules"
 | 
			
		||||
 | 
			
		||||
	"github.com/3nprob/cbind"
 | 
			
		||||
	"maunium.net/go/tcell"
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/debug"
 | 
			
		||||
	"maunium.net/go/gomuks/matrix/rooms"
 | 
			
		||||
)
 | 
			
		||||
@@ -56,6 +59,26 @@ type UserPreferences struct {
 | 
			
		||||
	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.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	UserID      id.UserID   `yaml:"mxid"`
 | 
			
		||||
@@ -85,6 +108,7 @@ type Config struct {
 | 
			
		||||
	AuthCache   AuthCache              `yaml:"-"`
 | 
			
		||||
	Rooms       *rooms.RoomCache       `yaml:"-"`
 | 
			
		||||
	PushRules   *pushrules.PushRuleset `yaml:"-"`
 | 
			
		||||
	Keybindings Keybindings            `yaml:"-"`
 | 
			
		||||
 | 
			
		||||
	nosave bool
 | 
			
		||||
}
 | 
			
		||||
@@ -152,6 +176,7 @@ func (config *Config) LoadAll() {
 | 
			
		||||
	config.LoadAuthCache()
 | 
			
		||||
	config.LoadPushRules()
 | 
			
		||||
	config.LoadPreferences()
 | 
			
		||||
	config.LoadKeybindings()
 | 
			
		||||
	err := config.Rooms.LoadList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
@@ -189,6 +214,70 @@ func (config *Config) SavePreferences() {
 | 
			
		||||
	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() {
 | 
			
		||||
	config.load("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
	"maunium.net/go/mauview"
 | 
			
		||||
	"maunium.net/go/tcell"
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
	"maunium.net/go/gomuks/debug"
 | 
			
		||||
	"maunium.net/go/gomuks/matrix/rooms"
 | 
			
		||||
)
 | 
			
		||||
@@ -120,12 +121,17 @@ func (fs *FuzzySearchModal) changeHandler(str string) {
 | 
			
		||||
 | 
			
		||||
func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
	highlights := fs.results.GetHighlights()
 | 
			
		||||
	switch event.Key() {
 | 
			
		||||
	case tcell.KeyEsc:
 | 
			
		||||
	kb := config.Keybind{
 | 
			
		||||
		Key: event.Key(),
 | 
			
		||||
		Ch:  event.Rune(),
 | 
			
		||||
		Mod: event.Modifiers(),
 | 
			
		||||
	}
 | 
			
		||||
	switch fs.parent.config.Keybindings.Modal[kb] {
 | 
			
		||||
	case "cancel":
 | 
			
		||||
		// Close room finder
 | 
			
		||||
		fs.parent.HideModal()
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyTab:
 | 
			
		||||
	case "select_next":
 | 
			
		||||
		// Cycle highlighted area to next match
 | 
			
		||||
		if len(highlights) > 0 {
 | 
			
		||||
			fs.selected = (fs.selected + 1) % len(fs.matches)
 | 
			
		||||
@@ -133,7 +139,7 @@ func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
			fs.results.ScrollToHighlight()
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyBacktab:
 | 
			
		||||
	case "select_prev":
 | 
			
		||||
		if len(highlights) > 0 {
 | 
			
		||||
			fs.selected = (fs.selected - 1) % len(fs.matches)
 | 
			
		||||
			if fs.selected < 0 {
 | 
			
		||||
@@ -143,7 +149,7 @@ func (fs *FuzzySearchModal) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
			fs.results.ScrollToHighlight()
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyEnter:
 | 
			
		||||
	case "confirm":
 | 
			
		||||
		// Switch room to currently selected room
 | 
			
		||||
		if len(highlights) > 0 {
 | 
			
		||||
			debug.Print("Fuzzy Selected Room:", fs.roomList[fs.matches[fs.selected].OriginalIndex].GetTitle())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package ui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
	"maunium.net/go/tcell"
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/mauview"
 | 
			
		||||
@@ -94,7 +95,12 @@ func NewHelpModal(parent *MainView) *HelpModal {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
	"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/util"
 | 
			
		||||
	"maunium.net/go/gomuks/matrix/muksevt"
 | 
			
		||||
@@ -339,41 +339,44 @@ func (view *RoomView) ClearAllContext() {
 | 
			
		||||
 | 
			
		||||
func (view *RoomView) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
	msgView := view.MessageView()
 | 
			
		||||
	kb := config.Keybind{
 | 
			
		||||
		Key: event.Key(),
 | 
			
		||||
		Ch:  event.Rune(),
 | 
			
		||||
		Mod: event.Modifiers(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if view.selecting {
 | 
			
		||||
		k := event.Key()
 | 
			
		||||
		c := event.Rune()
 | 
			
		||||
		switch {
 | 
			
		||||
		case k == tcell.KeyEscape || c == 'h':
 | 
			
		||||
		switch view.config.Keybindings.Visual[kb] {
 | 
			
		||||
		case "clear":
 | 
			
		||||
			view.ClearAllContext()
 | 
			
		||||
		case k == tcell.KeyUp || c == 'k':
 | 
			
		||||
		case "select_prev":
 | 
			
		||||
			view.SelectPrevious()
 | 
			
		||||
		case k == tcell.KeyDown || c == 'j':
 | 
			
		||||
		case "select_next":
 | 
			
		||||
			view.SelectNext()
 | 
			
		||||
		case k == tcell.KeyEnter || c == 'l':
 | 
			
		||||
		case "confirm":
 | 
			
		||||
			view.OnSelect(msgView.selected)
 | 
			
		||||
		default:
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	switch event.Key() {
 | 
			
		||||
	case tcell.KeyEscape:
 | 
			
		||||
 | 
			
		||||
	switch view.config.Keybindings.Room[kb] {
 | 
			
		||||
	case "clear":
 | 
			
		||||
		view.ClearAllContext()
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyPgUp:
 | 
			
		||||
	case "scroll_up":
 | 
			
		||||
		if msgView.IsAtTop() {
 | 
			
		||||
			go view.parent.LoadHistory(view.Room.ID)
 | 
			
		||||
		}
 | 
			
		||||
		msgView.AddScrollOffset(+msgView.Height() / 2)
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyPgDn:
 | 
			
		||||
	case "scroll_down":
 | 
			
		||||
		msgView.AddScrollOffset(-msgView.Height() / 2)
 | 
			
		||||
		return true
 | 
			
		||||
	case tcell.KeyEnter:
 | 
			
		||||
		if (event.Modifiers()&tcell.ModShift == 0 && event.Modifiers()&tcell.ModCtrl == 0) != (view.config.Preferences.AltEnterToSend) {
 | 
			
		||||
			view.InputSubmit(view.input.GetText())
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	case "send":
 | 
			
		||||
		view.InputSubmit(view.input.GetText())
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return view.input.OnKeyEvent(event)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
	"maunium.net/go/mauview"
 | 
			
		||||
	"maunium.net/go/tcell"
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
	"maunium.net/go/gomuks/debug"
 | 
			
		||||
	"maunium.net/go/mautrix/crypto"
 | 
			
		||||
	"maunium.net/go/mautrix/event"
 | 
			
		||||
@@ -207,8 +208,13 @@ func (vm *VerificationModal) OnSuccess() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (vm *VerificationModal) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
	kb := config.Keybind{
 | 
			
		||||
		Key: event.Key(),
 | 
			
		||||
		Ch:  event.Rune(),
 | 
			
		||||
		Mod: event.Modifiers(),
 | 
			
		||||
	}
 | 
			
		||||
	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()
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
@@ -217,7 +223,7 @@ func (vm *VerificationModal) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
		debug.Print("Ignoring pre-emoji key event")
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if event.Key() == tcell.KeyEnter {
 | 
			
		||||
	if vm.parent.config.Keybindings.Modal[kb] == "confirm" {
 | 
			
		||||
		text := strings.ToLower(strings.TrimSpace(vm.inputBar.GetText()))
 | 
			
		||||
		if text == "yes" {
 | 
			
		||||
			debug.Print("Confirming verification")
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"maunium.net/go/gomuks/config"
 | 
			
		||||
	"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/matrix/rooms"
 | 
			
		||||
	"maunium.net/go/gomuks/ui/widget"
 | 
			
		||||
@@ -170,33 +170,34 @@ func (view *MainView) OnKeyEvent(event mauview.KeyEvent) bool {
 | 
			
		||||
		return view.modal.OnKeyEvent(event)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k := event.Key()
 | 
			
		||||
	c := event.Rune()
 | 
			
		||||
	if event.Modifiers() == tcell.ModCtrl || event.Modifiers() == tcell.ModAlt {
 | 
			
		||||
		switch {
 | 
			
		||||
		case k == tcell.KeyDown:
 | 
			
		||||
			view.SwitchRoom(view.roomList.Next())
 | 
			
		||||
		case k == tcell.KeyUp:
 | 
			
		||||
			view.SwitchRoom(view.roomList.Previous())
 | 
			
		||||
		case c == 'k' || k == tcell.KeyCtrlK:
 | 
			
		||||
			view.ShowModal(NewFuzzySearchModal(view, 42, 12))
 | 
			
		||||
		case k == tcell.KeyHome:
 | 
			
		||||
			msgView := view.currentRoom.MessageView()
 | 
			
		||||
			msgView.AddScrollOffset(msgView.TotalHeight())
 | 
			
		||||
		case k == tcell.KeyEnd:
 | 
			
		||||
			msgView := view.currentRoom.MessageView()
 | 
			
		||||
			msgView.AddScrollOffset(-msgView.TotalHeight())
 | 
			
		||||
		case k == tcell.KeyEnter:
 | 
			
		||||
			return view.flex.OnKeyEvent(tcell.NewEventKey(tcell.KeyEnter, '\n', event.Modifiers()|tcell.ModShift, ""))
 | 
			
		||||
		case c == 'a':
 | 
			
		||||
			view.SwitchRoom(view.roomList.NextWithActivity())
 | 
			
		||||
		case c == 'l' || k == tcell.KeyCtrlL:
 | 
			
		||||
			view.ShowBare(view.currentRoom)
 | 
			
		||||
		default:
 | 
			
		||||
			goto defaultHandler
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	kb := config.Keybind{
 | 
			
		||||
		Key: event.Key(),
 | 
			
		||||
		Ch:  event.Rune(),
 | 
			
		||||
		Mod: event.Modifiers(),
 | 
			
		||||
	}
 | 
			
		||||
	switch view.config.Keybindings.Main[kb] {
 | 
			
		||||
	case "next_room":
 | 
			
		||||
		view.SwitchRoom(view.roomList.Next())
 | 
			
		||||
	case "prev_room":
 | 
			
		||||
		view.SwitchRoom(view.roomList.Previous())
 | 
			
		||||
	case "search_rooms":
 | 
			
		||||
		view.ShowModal(NewFuzzySearchModal(view, 42, 12))
 | 
			
		||||
	case "scroll_up":
 | 
			
		||||
		msgView := view.currentRoom.MessageView()
 | 
			
		||||
		msgView.AddScrollOffset(msgView.TotalHeight())
 | 
			
		||||
	case "scroll_down":
 | 
			
		||||
		msgView := view.currentRoom.MessageView()
 | 
			
		||||
		msgView.AddScrollOffset(-msgView.TotalHeight())
 | 
			
		||||
	case "add_newline":
 | 
			
		||||
		return view.flex.OnKeyEvent(tcell.NewEventKey(tcell.KeyEnter, '\n', event.Modifiers()|tcell.ModShift, ""))
 | 
			
		||||
	case "next_active_room":
 | 
			
		||||
		view.SwitchRoom(view.roomList.NextWithActivity())
 | 
			
		||||
	case "show_bare":
 | 
			
		||||
		view.ShowBare(view.currentRoom)
 | 
			
		||||
	default:
 | 
			
		||||
		goto defaultHandler
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
defaultHandler:
 | 
			
		||||
	if view.config.Preferences.HideRoomList {
 | 
			
		||||
		return view.roomView.OnKeyEvent(event)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user