issue-9: finishing keybinding service and implemented controller
Have now got rebinding keys working with the "rebind" command. Still need to make sure key names are correct and implement rebinding as part of an RC file and add bindings for the table.
This commit is contained in:
parent
2f89610c51
commit
7c5bfd27a3
|
@ -98,15 +98,17 @@ func main() {
|
|||
},
|
||||
}
|
||||
|
||||
commandController := commandctrl.NewCommandController()
|
||||
keyBindingService := keybindings.NewService(defaultKeyBindings)
|
||||
_ = keyBindingService
|
||||
keyBindingController := controllers.NewKeyBindingController(keyBindingService)
|
||||
|
||||
commandController := commandctrl.NewCommandController()
|
||||
|
||||
model := ui.NewModel(
|
||||
tableReadController,
|
||||
tableWriteController,
|
||||
itemRendererService,
|
||||
commandController,
|
||||
keyBindingController,
|
||||
defaultKeyBindings,
|
||||
)
|
||||
|
||||
|
|
38
internal/dynamo-browse/controllers/keybinding.go
Normal file
38
internal/dynamo-browse/controllers/keybinding.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/audax/internal/common/ui/events"
|
||||
"github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type KeyBindingController struct {
|
||||
service *keybindings.Service
|
||||
}
|
||||
|
||||
func NewKeyBindingController(service *keybindings.Service) *KeyBindingController {
|
||||
return &KeyBindingController{service: service}
|
||||
}
|
||||
|
||||
func (kb *KeyBindingController) Rebind(newKey string, bindingName string) tea.Msg {
|
||||
err := kb.service.Rebind(newKey, bindingName, false)
|
||||
if err == nil {
|
||||
return events.SetStatus(fmt.Sprintf("Key '%v' now bound to '%v'", newKey, bindingName))
|
||||
}
|
||||
|
||||
var keyAlreadyBoundErr keybindings.KeyAlreadyBoundError
|
||||
if errors.As(err, &keyAlreadyBoundErr) {
|
||||
promptMsg := fmt.Sprintf("Key '%v' already bound to '%v'. Continue? ", keyAlreadyBoundErr.Key, keyAlreadyBoundErr.ExistingBindingName)
|
||||
return events.Confirm(promptMsg, func() tea.Msg {
|
||||
err := kb.service.Rebind(newKey, bindingName, true)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
return events.SetStatus(fmt.Sprintf("Key '%v' now bound to '%v'", newKey, bindingName))
|
||||
})
|
||||
}
|
||||
|
||||
return events.Error(err)
|
||||
}
|
14
internal/dynamo-browse/services/keybindings/errors.go
Normal file
14
internal/dynamo-browse/services/keybindings/errors.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
package keybindings
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type KeyAlreadyBoundError struct {
|
||||
Key string
|
||||
ExistingBindingName string
|
||||
}
|
||||
|
||||
func (e KeyAlreadyBoundError) Error() string {
|
||||
return fmt.Sprintf("key '%v' already bound to '%v'", e.Key, e.ExistingBindingName)
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
package keybindings
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/pkg/errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
keyBindingValue reflect.Value
|
||||
|
@ -17,16 +22,95 @@ func NewService(keyBinding any) *Service {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Service) Rebind(name string, key string) error {
|
||||
func (s *Service) Rebind(newKey string, name string, force bool) error {
|
||||
// Check if there already exists a binding
|
||||
if !force {
|
||||
var foundBinding = ""
|
||||
s.walkBindingFields(func(bindingName string, binding key.Binding) bool {
|
||||
for _, key := range binding.Keys() {
|
||||
if key == newKey {
|
||||
foundBinding = bindingName
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) findFieldForBinding(name string) reflect.Value {
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) findFieldForBindingInGroup(group reflect.Value, name string) reflect.Value {
|
||||
for i := 0; i < group.NumField(); i++ {
|
||||
group.Field(i).Type().
|
||||
if foundBinding != "" {
|
||||
return KeyAlreadyBoundError{Key: newKey, ExistingBindingName: foundBinding}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebind
|
||||
binding := s.findFieldForBinding(name)
|
||||
if binding == nil {
|
||||
return errors.Errorf("invalid binding: %v", name)
|
||||
}
|
||||
|
||||
*binding = key.NewBinding(key.WithKeys(newKey))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) findFieldForBinding(name string) *key.Binding {
|
||||
return s.findFieldForBindingInGroup(s.keyBindingValue, name)
|
||||
}
|
||||
|
||||
func (s *Service) findFieldForBindingInGroup(group reflect.Value, name string) *key.Binding {
|
||||
bindingName, bindingSuffix, _ := strings.Cut(name, ".")
|
||||
|
||||
groupType := group.Type()
|
||||
for i := 0; i < group.NumField(); i++ {
|
||||
fieldType := groupType.Field(i)
|
||||
|
||||
keymapTag := fieldType.Tag.Get("keymap")
|
||||
if keymapTag != bindingName {
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldType.Type.Kind() == reflect.Pointer && fieldType.Type.Elem().Kind() == reflect.Struct {
|
||||
return s.findFieldForBindingInGroup(group.Field(i).Elem(), bindingSuffix)
|
||||
}
|
||||
|
||||
binding, isBinding := group.Field(i).Addr().Interface().(*key.Binding)
|
||||
if !isBinding {
|
||||
return nil
|
||||
}
|
||||
return binding
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) walkBindingFields(fn func(name string, binding key.Binding) bool) {
|
||||
s.walkBindingFieldsInGroup(s.keyBindingValue, "", fn)
|
||||
}
|
||||
|
||||
func (s *Service) walkBindingFieldsInGroup(group reflect.Value, prefix string, fn func(name string, binding key.Binding) bool) bool {
|
||||
groupType := group.Type()
|
||||
for i := 0; i < group.NumField(); i++ {
|
||||
fieldType := groupType.Field(i)
|
||||
|
||||
keymapTag := fieldType.Tag.Get("keymap")
|
||||
|
||||
var fullName string
|
||||
if prefix != "" {
|
||||
fullName = prefix + "." + keymapTag
|
||||
} else {
|
||||
fullName = keymapTag
|
||||
}
|
||||
|
||||
if fieldType.Type.Kind() == reflect.Pointer && fieldType.Type.Elem().Kind() == reflect.Struct {
|
||||
if !s.walkBindingFieldsInGroup(group.Field(i).Elem(), fullName, fn) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
binding, isBinding := group.Field(i).Addr().Interface().(*key.Binding)
|
||||
if !isBinding {
|
||||
continue
|
||||
}
|
||||
if !fn(fullName, *binding) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package ui
|
|||
import "github.com/charmbracelet/bubbles/key"
|
||||
|
||||
type KeyBindings struct {
|
||||
View *ViewKeyBindings `keymap:"view,group"`
|
||||
View *ViewKeyBindings `keymap:"view"`
|
||||
}
|
||||
|
||||
type ViewKeyBindings struct {
|
||||
|
|
|
@ -54,6 +54,7 @@ func NewModel(
|
|||
wc *controllers.TableWriteController,
|
||||
itemRendererService *itemrenderer.Service,
|
||||
cc *commandctrl.CommandController,
|
||||
keyBindingController *controllers.KeyBindingController,
|
||||
defaultKeyMap *KeyBindings,
|
||||
) Model {
|
||||
uiStyles := styles.DefaultStyles
|
||||
|
@ -129,9 +130,12 @@ func NewModel(
|
|||
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
|
||||
},
|
||||
|
||||
//"rebind": func(args []string) tea.Msg {
|
||||
//
|
||||
//},
|
||||
"rebind": func(args []string) tea.Msg {
|
||||
if len(args) != 2 {
|
||||
return events.Error(errors.New("expected: name newKey"))
|
||||
}
|
||||
return keyBindingController.Rebind(args[0], args[1])
|
||||
},
|
||||
|
||||
// Aliases
|
||||
"sa": cc.Alias("set-attr"),
|
||||
|
|
Loading…
Reference in a new issue