diff --git a/pkg/gocui/gui.go b/pkg/gocui/gui.go index 15c454c88..1bc184625 100644 --- a/pkg/gocui/gui.go +++ b/pkg/gocui/gui.go @@ -2091,13 +2091,15 @@ func (g *Gui) isSuspended() bool { return g.suspended } -// matchView returns if the keybinding matches the current view (and the view's context) +// matchView returns if the keybinding matches the given view (and the view's context) func (g *Gui) matchView(v *View, kb *keybinding) bool { - // if the user is typing in a field, ignore char keys if v == nil { return false } - if v.Editable && kb.key.IsPrintable() { + // If the user is typing in a field, printable keys are theirs to type, so no + // keybinding gets a look at them: not the field's own, and not those of the + // view it is embedded in either. + if field := g.currentView; field != nil && field.Editable && !field.KeybindOnEdit && kb.key.IsPrintable() { return false } if kb.viewName != v.name { diff --git a/pkg/gocui/parent_view_test.go b/pkg/gocui/parent_view_test.go index 993eb08c1..510acdbbb 100644 --- a/pkg/gocui/parent_view_test.go +++ b/pkg/gocui/parent_view_test.go @@ -56,6 +56,52 @@ func TestFirstMatchingKeybindingOfParentViewWins(t *testing.T) { assert.Equal(t, []string{"first"}, pressed) } +func TestPrintableKeysGoToTheFieldBeingTypedIn(t *testing.T) { + for _, test := range []struct { + name string + keybindOnEdit bool + declineKeybinding bool + expectedPresses int + expectedEdits int + }{ + {name: "the field gets the key", expectedEdits: 1}, + {name: "the parent view gets the key", keybindOnEdit: true, expectedPresses: 1}, + { + name: "the field gets the key the parent view declined", + keybindOnEdit: true, + declineKeybinding: true, + expectedPresses: 1, + expectedEdits: 1, + }, + } { + t.Run(test.name, func(t *testing.T) { + g := newTestGui(t) + parent, child := setupParentAndChildView(t, g) + child.Editable = true + child.KeybindOnEdit = test.keybindOnEdit + + edits := 0 + child.Editor = EditorFunc(func(*View, Key) bool { + edits++ + return true + }) + presses := 0 + g.SetKeybinding(parent.Name(), NewKeyRune('j'), func(*Gui, *View) error { + presses++ + if test.declineKeybinding { + return ErrKeybindingNotHandled + } + return nil + }) + + assert.NoError(t, g.onKey(&GocuiEvent{Type: eventKey, Key: NewKeyRune('j')})) + + assert.Equal(t, test.expectedPresses, presses) + assert.Equal(t, test.expectedEdits, edits) + }) + } +} + func TestUnhandledKeybindingOfParentViewFallsThroughToEditor(t *testing.T) { g := newTestGui(t) parent, child := setupParentAndChildView(t, g)