1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-30 03:23:08 +03:00

more refactoring

This commit is contained in:
Jesse Duffield
2022-02-05 14:42:56 +11:00
parent 8e3484d8e9
commit 482bdc4f1e
30 changed files with 372 additions and 227 deletions

View File

@ -124,7 +124,7 @@ func formatBinding(binding *types.Binding) string {
func getBindingSections(mApp *app.App) []*bindingSection { func getBindingSections(mApp *app.App) []*bindingSection {
bindingSections := []*bindingSection{} bindingSections := []*bindingSection{}
bindings := mApp.Gui.GetInitialKeybindings() bindings, _ := mApp.Gui.GetInitialKeybindings()
type contextAndViewType struct { type contextAndViewType struct {
subtitle string subtitle string

View File

@ -257,7 +257,7 @@ func (gui *Gui) handleToggleCommitFileDirCollapsed() error {
func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesContextOpts) error { func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesContextOpts) error {
// sometimes the commitFiles view is already shown in another window, so we need to ensure that window // sometimes the commitFiles view is already shown in another window, so we need to ensure that window
// no longer considers the commitFiles view as its main view. // no longer considers the commitFiles view as its main view.
gui.resetWindowForView(gui.Views.CommitFiles) gui.resetWindowContext(gui.State.Contexts.CommitFiles)
gui.State.Contexts.CommitFiles.SetSelectedLineIdx(0) gui.State.Contexts.CommitFiles.SetSelectedLineIdx(0)
gui.State.Contexts.CommitFiles.SetRefName(opts.RefName) gui.State.Contexts.CommitFiles.SetRefName(opts.RefName)

View File

@ -60,6 +60,10 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
return errors.New("cannot pass multiple opts to pushContext") return errors.New("cannot pass multiple opts to pushContext")
} }
if c.GetKey() == context.GLOBAL_CONTEXT_KEY {
return errors.New("Cannot push global context")
}
gui.State.ContextManager.Lock() gui.State.ContextManager.Lock()
// push onto stack // push onto stack
@ -112,6 +116,8 @@ func (gui *Gui) returnFromContext() error {
gui.State.ContextManager.ContextStack = gui.State.ContextManager.ContextStack[:n] gui.State.ContextManager.ContextStack = gui.State.ContextManager.ContextStack[:n]
gui.g.SetCurrentContext(string(newContext.GetKey()))
gui.State.ContextManager.Unlock() gui.State.ContextManager.Unlock()
if err := gui.deactivateContext(currentContext); err != nil { if err := gui.deactivateContext(currentContext); err != nil {
@ -146,12 +152,7 @@ func (gui *Gui) deactivateContext(c types.Context) error {
// if the context's view is set to another context we do nothing. // if the context's view is set to another context we do nothing.
// if the context's view is the current view we trigger a focus; re-selecting the current item. // if the context's view is the current view we trigger a focus; re-selecting the current item.
func (gui *Gui) postRefreshUpdate(c types.Context) error { func (gui *Gui) postRefreshUpdate(c types.Context) error {
v, err := gui.g.View(c.GetViewName()) if gui.State.ViewContextMap[c.GetViewName()].GetKey() != c.GetKey() {
if err != nil {
return nil
}
if types.ContextKey(v.Context) != c.GetKey() {
return nil return nil
} }
@ -174,19 +175,18 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
if err != nil { if err != nil {
return err return err
} }
originalViewContextKey := types.ContextKey(v.Context) originalViewContextKey := gui.State.ViewContextMap[viewName].GetKey()
// ensure that any other window for which this view was active is now set to the default for that window.
gui.setViewAsActiveForWindow(v)
if viewName == "main" {
gui.changeMainViewsContext(c.GetKey())
} else {
gui.changeMainViewsContext(context.MAIN_NORMAL_CONTEXT_KEY)
}
gui.setWindowContext(c)
gui.setViewTabForContext(c) gui.setViewTabForContext(c)
if viewName == "main" {
gui.changeMainViewsContext(c)
} else {
gui.changeMainViewsContext(gui.State.Contexts.Normal)
}
gui.g.SetCurrentContext(string(c.GetKey()))
if _, err := gui.g.SetCurrentView(viewName); err != nil { if _, err := gui.g.SetCurrentView(viewName); err != nil {
return err return err
} }
@ -200,7 +200,7 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
} }
} }
v.Context = string(c.GetKey()) gui.State.ViewContextMap[viewName] = c
gui.g.Cursor = v.Editable gui.g.Cursor = v.Editable
@ -310,21 +310,6 @@ func (gui *Gui) defaultSideContext() types.Context {
} }
} }
// remove the need to do this: always use a mapping
func (gui *Gui) setInitialViewContexts() {
// arguably we should only have our ViewContextMap and we should do away with
// contexts on views, or vice versa
for viewName, context := range gui.State.ViewContextMap {
// see if the view exists. If it does, set the context on it
view, err := gui.g.View(viewName)
if err != nil {
continue
}
view.Context = string(context.GetKey())
}
}
// getFocusLayout returns a manager function for when view gain and lose focus // getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error { func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var previousView *gocui.View var previousView *gocui.View
@ -364,7 +349,7 @@ func (gui *Gui) onViewFocusLost(oldView *gocui.View, newView *gocui.View) error
_ = oldView.SetOriginX(0) _ = oldView.SetOriginX(0)
if oldView == gui.Views.CommitFiles && newView != gui.Views.Main && newView != gui.Views.Secondary && newView != gui.Views.Search { if oldView == gui.Views.CommitFiles && newView != gui.Views.Main && newView != gui.Views.Secondary && newView != gui.Views.Search {
gui.resetWindowForView(gui.Views.CommitFiles) gui.resetWindowContext(gui.State.Contexts.CommitFiles)
if err := gui.deactivateContext(gui.State.Contexts.CommitFiles); err != nil { if err := gui.deactivateContext(gui.State.Contexts.CommitFiles); err != nil {
return err return err
} }
@ -377,20 +362,20 @@ func (gui *Gui) onViewFocusLost(oldView *gocui.View, newView *gocui.View) error
// which currently just means a context that affects both the main and secondary views // which currently just means a context that affects both the main and secondary views
// other views can have their context changed directly but this function helps // other views can have their context changed directly but this function helps
// keep the main and secondary views in sync // keep the main and secondary views in sync
func (gui *Gui) changeMainViewsContext(contextKey types.ContextKey) { func (gui *Gui) changeMainViewsContext(c types.Context) {
if gui.State.MainContext == contextKey { if gui.State.MainContext == c.GetKey() {
return return
} }
switch contextKey { switch c.GetKey() {
case context.MAIN_NORMAL_CONTEXT_KEY, context.MAIN_PATCH_BUILDING_CONTEXT_KEY, context.MAIN_STAGING_CONTEXT_KEY, context.MAIN_MERGING_CONTEXT_KEY: case context.MAIN_NORMAL_CONTEXT_KEY, context.MAIN_PATCH_BUILDING_CONTEXT_KEY, context.MAIN_STAGING_CONTEXT_KEY, context.MAIN_MERGING_CONTEXT_KEY:
gui.Views.Main.Context = string(contextKey) gui.State.ViewContextMap[gui.Views.Main.Name()] = c
gui.Views.Secondary.Context = string(contextKey) gui.State.ViewContextMap[gui.Views.Secondary.Name()] = c
default: default:
panic(fmt.Sprintf("unknown context for main: %s", contextKey)) panic(fmt.Sprintf("unknown context for main: %s", c.GetKey()))
} }
gui.State.MainContext = contextKey gui.State.MainContext = c.GetKey()
} }
func (gui *Gui) viewTabNames(viewName string) []string { func (gui *Gui) viewTabNames(viewName string) []string {
@ -452,8 +437,11 @@ func (gui *Gui) contextForContextKey(contextKey types.ContextKey) (types.Context
} }
func (gui *Gui) rerenderView(view *gocui.View) error { func (gui *Gui) rerenderView(view *gocui.View) error {
contextKey := types.ContextKey(view.Context) context, ok := gui.State.ViewContextMap[view.Name()]
context := gui.mustContextForContextKey(contextKey)
if !ok {
panic("no context set against view " + view.Name())
}
return context.HandleRender() return context.HandleRender()
} }
@ -467,6 +455,10 @@ func (gui *Gui) getSideContextSelectedItemId() string {
return currentSideContext.GetSelectedItemId() return currentSideContext.GetSelectedItemId()
} }
func (gui *Gui) isContextVisible(c types.Context) bool {
return gui.State.WindowViewNameMap[c.GetWindowName()] == c.GetViewName() && gui.State.ViewContextMap[c.GetViewName()].GetKey() == c.GetKey()
}
// currently unused // currently unused
// func (gui *Gui) getCurrentSideView() *gocui.View { // func (gui *Gui) getCurrentSideView() *gocui.View {
// currentSideContext := gui.currentSideContext() // currentSideContext := gui.currentSideContext()

View File

@ -1,6 +1,7 @@
package context package context
import ( import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
) )
@ -12,7 +13,7 @@ type BaseContext struct {
onGetOptionsMap func() map[string]string onGetOptionsMap func() map[string]string
keybindingsFns []types.KeybindingsFn keybindingsFns []types.KeybindingsFn
keybindings []*types.Binding mouseKeybindingsFns []types.MouseKeybindingsFn
*ParentContextMgr *ParentContextMgr
} }
@ -80,3 +81,18 @@ func (self *BaseContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Bin
func (self *BaseContext) AddKeybindingsFn(fn types.KeybindingsFn) { func (self *BaseContext) AddKeybindingsFn(fn types.KeybindingsFn) {
self.keybindingsFns = append(self.keybindingsFns, fn) self.keybindingsFns = append(self.keybindingsFns, fn)
} }
func (self *BaseContext) AddMouseKeybindingsFn(fn types.MouseKeybindingsFn) {
self.mouseKeybindingsFns = append(self.mouseKeybindingsFns, fn)
}
func (self *BaseContext) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
bindings := []*gocui.ViewMouseBinding{}
for i := range self.mouseKeybindingsFns {
// the first binding in the bindings array takes precedence but we want the
// last keybindingsFn to take precedence to we add them in reverse
bindings = append(bindings, self.mouseKeybindingsFns[len(self.mouseKeybindingsFns)-1-i](opts)...)
}
return bindings
}

View File

@ -3,6 +3,7 @@ package context
import "github.com/jesseduffield/lazygit/pkg/gui/types" import "github.com/jesseduffield/lazygit/pkg/gui/types"
const ( const (
GLOBAL_CONTEXT_KEY types.ContextKey = "global"
STATUS_CONTEXT_KEY types.ContextKey = "status" STATUS_CONTEXT_KEY types.ContextKey = "status"
FILES_CONTEXT_KEY types.ContextKey = "files" FILES_CONTEXT_KEY types.ContextKey = "files"
LOCAL_BRANCHES_CONTEXT_KEY types.ContextKey = "localBranches" LOCAL_BRANCHES_CONTEXT_KEY types.ContextKey = "localBranches"
@ -29,6 +30,7 @@ const (
) )
var AllContextKeys = []types.ContextKey{ var AllContextKeys = []types.ContextKey{
GLOBAL_CONTEXT_KEY,
STATUS_CONTEXT_KEY, STATUS_CONTEXT_KEY,
FILES_CONTEXT_KEY, FILES_CONTEXT_KEY,
LOCAL_BRANCHES_CONTEXT_KEY, LOCAL_BRANCHES_CONTEXT_KEY,
@ -55,6 +57,7 @@ var AllContextKeys = []types.ContextKey{
} }
type ContextTree struct { type ContextTree struct {
Global types.Context
Status types.Context Status types.Context
Files *WorkingTreeContext Files *WorkingTreeContext
Submodules types.IListContext Submodules types.IListContext

View File

@ -7,6 +7,7 @@ import (
func (gui *Gui) allContexts() []types.Context { func (gui *Gui) allContexts() []types.Context {
return []types.Context{ return []types.Context{
gui.State.Contexts.Global,
gui.State.Contexts.Status, gui.State.Contexts.Status,
gui.State.Contexts.Files, gui.State.Contexts.Files,
gui.State.Contexts.Submodules, gui.State.Contexts.Submodules,
@ -34,12 +35,23 @@ func (gui *Gui) allContexts() []types.Context {
func (gui *Gui) contextTree() *context.ContextTree { func (gui *Gui) contextTree() *context.ContextTree {
return &context.ContextTree{ return &context.ContextTree{
Global: NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.GLOBAL_CONTEXT,
ViewName: "",
WindowName: "",
Key: context.GLOBAL_CONTEXT_KEY,
}),
NewSimpleContextOpts{
OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain),
},
),
Status: NewSimpleContext( Status: NewSimpleContext(
context.NewBaseContext(context.NewBaseContextOpts{ context.NewBaseContext(context.NewBaseContextOpts{
Kind: types.SIDE_CONTEXT, Kind: types.SIDE_CONTEXT,
ViewName: "status", ViewName: "status",
Key: context.STATUS_CONTEXT_KEY,
WindowName: "status", WindowName: "status",
Key: context.STATUS_CONTEXT_KEY,
}), }),
NewSimpleContextOpts{ NewSimpleContextOpts{
OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain), OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain),

View File

@ -0,0 +1,10 @@
package controllers
import "github.com/jesseduffield/lazygit/pkg/gui/types"
func AttachControllers(context types.Context, controllers ...types.IController) {
for _, controller := range controllers {
context.AddKeybindingsFn(controller.GetKeybindings)
context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
}
}

View File

@ -0,0 +1,16 @@
package controllers
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type baseController struct{}
func (self *baseController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
return nil
}
func (self *baseController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
return nil
}

View File

@ -11,6 +11,8 @@ import (
) )
type BisectController struct { type BisectController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context types.IListContext
git *commands.GitCommand git *commands.GitCommand
@ -32,6 +34,7 @@ func NewBisectController(
getCommits func() []*models.Commit, getCommits func() []*models.Commit,
) *BisectController { ) *BisectController {
return &BisectController{ return &BisectController{
baseController: baseController{},
c: c, c: c,
context: context, context: context,
git: git, git: git,

View File

@ -190,6 +190,21 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
} }
} }
func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
return []*gocui.ViewMouseBinding{
{
ViewName: "main",
Key: gocui.MouseLeft,
Handler: self.onClickMain,
},
{
ViewName: "secondary",
Key: gocui.MouseLeft,
Handler: self.onClickSecondary,
},
}
}
func (self *FilesController) press(node *filetree.FileNode) error { func (self *FilesController) press(node *filetree.FileNode) error {
if node.IsLeaf() { if node.IsLeaf() {
file := node.File file := node.File
@ -672,3 +687,13 @@ func (self *FilesController) handleStashSave(stashFunc func(message string) erro
}, },
}) })
} }
func (self *FilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
clickedViewLineIdx := opts.Cy + opts.Oy
return self.EnterFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: clickedViewLineIdx})
}
func (self *FilesController) onClickSecondary(opts gocui.ViewMouseBindingOpts) error {
clickedViewLineIdx := opts.Cy + opts.Oy
return self.EnterFile(types.OnFocusOpts{ClickedViewName: "secondary", ClickedViewLineIdx: clickedViewLineIdx})
}

View File

@ -7,6 +7,8 @@ import (
) )
type GlobalController struct { type GlobalController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
os *oscommands.OSCommand os *oscommands.OSCommand
} }
@ -16,6 +18,7 @@ func NewGlobalController(
os *oscommands.OSCommand, os *oscommands.OSCommand,
) *GlobalController { ) *GlobalController {
return &GlobalController{ return &GlobalController{
baseController: baseController{},
c: c, c: c,
os: os, os: os,
} }

View File

@ -22,6 +22,7 @@ type (
) )
type LocalCommitsController struct { type LocalCommitsController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context types.IListContext
os *oscommands.OSCommand os *oscommands.OSCommand
@ -68,6 +69,7 @@ func NewLocalCommitsController(
setShowWholeGitGraph func(bool), setShowWholeGitGraph func(bool),
) *LocalCommitsController { ) *LocalCommitsController {
return &LocalCommitsController{ return &LocalCommitsController{
baseController: baseController{},
c: c, c: c,
context: context, context: context,
os: os, os: os,

View File

@ -6,6 +6,8 @@ import (
) )
type MenuController struct { type MenuController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context types.IListContext
@ -20,6 +22,8 @@ func NewMenuController(
getSelectedMenuItem func() *types.MenuItem, getSelectedMenuItem func() *types.MenuItem,
) *MenuController { ) *MenuController {
return &MenuController{ return &MenuController{
baseController: baseController{},
c: c, c: c,
context: context, context: context,
getSelectedMenuItem: getSelectedMenuItem, getSelectedMenuItem: getSelectedMenuItem,

View File

@ -10,6 +10,8 @@ import (
) )
type RemotesController struct { type RemotesController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context types.IListContext
git *commands.GitCommand git *commands.GitCommand
@ -30,6 +32,7 @@ func NewRemotesController(
setRemoteBranches func([]*models.RemoteBranch), setRemoteBranches func([]*models.RemoteBranch),
) *RemotesController { ) *RemotesController {
return &RemotesController{ return &RemotesController{
baseController: baseController{},
c: c, c: c,
git: git, git: git,
contexts: contexts, contexts: contexts,

View File

@ -13,6 +13,8 @@ import (
) )
type SubmodulesController struct { type SubmodulesController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context types.IListContext context types.IListContext
git *commands.GitCommand git *commands.GitCommand
@ -31,6 +33,7 @@ func NewSubmodulesController(
getSelectedSubmodule func() *models.SubmoduleConfig, getSelectedSubmodule func() *models.SubmoduleConfig,
) *SubmodulesController { ) *SubmodulesController {
return &SubmodulesController{ return &SubmodulesController{
baseController: baseController{},
c: c, c: c,
context: context, context: context,
git: git, git: git,

View File

@ -11,10 +11,8 @@ import (
) )
type SyncController struct { type SyncController struct {
// I've said publicly that I'm against single-letter variable names but in this baseController
// case I would actually prefer a _zero_ letter variable name in the form of
// struct embedding, but Go does not allow hiding public fields in an embedded struct
// to the client
c *types.ControllerCommon c *types.ControllerCommon
git *commands.GitCommand git *commands.GitCommand
@ -35,6 +33,7 @@ func NewSyncController(
CheckMergeOrRebase func(error) error, CheckMergeOrRebase func(error) error,
) *SyncController { ) *SyncController {
return &SyncController{ return &SyncController{
baseController: baseController{},
c: c, c: c,
git: git, git: git,

View File

@ -9,6 +9,8 @@ import (
) )
type TagsController struct { type TagsController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
context *context.TagsContext context *context.TagsContext
git *commands.GitCommand git *commands.GitCommand
@ -35,6 +37,7 @@ func NewTagsController(
switchToSubCommitsContext func(string) error, switchToSubCommitsContext func(string) error,
) *TagsController { ) *TagsController {
return &TagsController{ return &TagsController{
baseController: baseController{},
c: c, c: c,
context: context, context: context,
git: git, git: git,

View File

@ -19,6 +19,8 @@ import (
// two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way. // two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way.
type UndoController struct { type UndoController struct {
baseController
c *types.ControllerCommon c *types.ControllerCommon
git *commands.GitCommand git *commands.GitCommand
@ -39,6 +41,7 @@ func NewUndoController(
getFilteredReflogCommits func() []*models.Commit, getFilteredReflogCommits func() []*models.Commit,
) *UndoController { ) *UndoController {
return &UndoController{ return &UndoController{
baseController: baseController{},
c: c, c: c,
git: git, git: git,
refsHelper: refsHelper, refsHelper: refsHelper,

View File

@ -182,11 +182,6 @@ func (gui *Gui) handleRefresh() error {
func (gui *Gui) handleMouseDownMain() error { func (gui *Gui) handleMouseDownMain() error {
switch gui.currentSideContext() { switch gui.currentSideContext() {
case gui.State.Contexts.Files:
// set filename, set primary/secondary selected, set line number, then switch context
// I'll need to know it was changed though.
// Could I pass something along to the context change?
return gui.Controllers.Files.EnterFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: gui.Views.Main.SelectedLineIdx()})
case gui.State.Contexts.CommitFiles: case gui.State.Contexts.CommitFiles:
return gui.enterCommitFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: gui.Views.Main.SelectedLineIdx()}) return gui.enterCommitFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: gui.Views.Main.SelectedLineIdx()})
} }
@ -194,15 +189,6 @@ func (gui *Gui) handleMouseDownMain() error {
return nil return nil
} }
func (gui *Gui) handleMouseDownSecondary() error {
switch gui.g.CurrentView() {
case gui.Views.Files:
return gui.Controllers.Files.EnterFile(types.OnFocusOpts{ClickedViewName: "secondary", ClickedViewLineIdx: gui.Views.Secondary.SelectedLineIdx()})
}
return nil
}
func (gui *Gui) fetch() (err error) { func (gui *Gui) fetch() (err error) {
gui.c.LogAction("Fetch") gui.c.LogAction("Fetch")
err = gui.git.Sync.Fetch(git_commands.FetchOptions{}) err = gui.git.Sync.Fetch(git_commands.FetchOptions{})

View File

@ -590,6 +590,15 @@ func (gui *Gui) resetControllers() {
gui.getSelectedSubmodule, gui.getSelectedSubmodule,
) )
bisectController := controllers.NewBisectController(
controllerCommon,
gui.State.Contexts.BranchCommits,
gui.git,
gui.helpers.Bisect,
gui.getSelectedLocalCommit,
func() []*models.Commit { return gui.State.Model.Commits },
)
gui.Controllers = Controllers{ gui.Controllers = Controllers{
Submodules: submodulesController, Submodules: submodulesController,
Global: controllers.NewGlobalController( Global: controllers.NewGlobalController(
@ -660,14 +669,6 @@ func (gui *Gui) resetControllers() {
gui.State.Contexts.Menu, gui.State.Contexts.Menu,
gui.getSelectedMenuItem, gui.getSelectedMenuItem,
), ),
Bisect: controllers.NewBisectController(
controllerCommon,
gui.State.Contexts.BranchCommits,
gui.git,
gui.helpers.Bisect,
gui.getSelectedLocalCommit,
func() []*models.Commit { return gui.State.Model.Commits },
),
Undo: controllers.NewUndoController( Undo: controllers.NewUndoController(
controllerCommon, controllerCommon,
gui.git, gui.git,
@ -678,17 +679,13 @@ func (gui *Gui) resetControllers() {
Sync: syncController, Sync: syncController,
} }
gui.State.Contexts.Submodules.AddKeybindingsFn(gui.Controllers.Submodules.GetKeybindings) controllers.AttachControllers(gui.State.Contexts.Files, gui.Controllers.Files)
gui.Controllers.Files.Attach(gui.State.Contexts.Files) controllers.AttachControllers(gui.State.Contexts.Tags, gui.Controllers.Tags)
gui.State.Contexts.Files.AddKeybindingsFn(gui.Controllers.Files.GetKeybindings) controllers.AttachControllers(gui.State.Contexts.Submodules, gui.Controllers.Submodules)
gui.State.Contexts.Tags.AddKeybindingsFn(gui.Controllers.Tags.GetKeybindings) controllers.AttachControllers(gui.State.Contexts.BranchCommits, gui.Controllers.LocalCommits, bisectController)
// TODO: commit to one name here: local commits or branch commits controllers.AttachControllers(gui.State.Contexts.Remotes, gui.Controllers.Remotes)
gui.State.Contexts.BranchCommits.AddKeybindingsFn(gui.Controllers.LocalCommits.GetKeybindings) controllers.AttachControllers(gui.State.Contexts.Menu, gui.Controllers.Menu)
gui.State.Contexts.BranchCommits.AddKeybindingsFn(gui.Controllers.Bisect.GetKeybindings) controllers.AttachControllers(gui.State.Contexts.Global, gui.Controllers.Sync, gui.Controllers.Undo, gui.Controllers.Global)
gui.State.Contexts.Remotes.AddKeybindingsFn(gui.Controllers.Remotes.GetKeybindings)
gui.State.Contexts.Menu.AddKeybindingsFn(gui.Controllers.Menu.GetKeybindings)
gui.State.Contexts.Menu.AddKeybindingsFn(gui.Controllers.Menu.GetKeybindings)
// TODO: handle global contexts
} }
var RuneReplacements = map[rune]string{ var RuneReplacements = map[rune]string{

View File

@ -39,7 +39,6 @@ import (
// original playback speed. Speed may be a decimal. // original playback speed. Speed may be a decimal.
func Test(t *testing.T) { func Test(t *testing.T) {
return
mode := integration.GetModeFromEnv() mode := integration.GetModeFromEnv()
speedEnv := os.Getenv("SPEED") speedEnv := os.Getenv("SPEED")
includeSkipped := os.Getenv("INCLUDE_SKIPPED") != "" includeSkipped := os.Getenv("INCLUDE_SKIPPED") != ""

View File

@ -194,8 +194,7 @@ func (gui *Gui) noPopupPanel(f func() error) func() error {
} }
} }
// GetInitialKeybindings is a function. func (gui *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBinding) {
func (gui *Gui) GetInitialKeybindings() []*types.Binding {
config := gui.c.UserConfig.Keybinding config := gui.c.UserConfig.Keybinding
guards := types.KeybindingGuards{ guards := types.KeybindingGuards{
@ -306,12 +305,6 @@ func (gui *Gui) GetInitialKeybindings() []*types.Binding {
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu, Handler: gui.handleCreateOptionsMenu,
}, },
{
ViewName: "",
Key: gocui.MouseMiddle,
Modifier: gocui.ModNone,
Handler: gui.handleCreateOptionsMenu,
},
{ {
ViewName: "status", ViewName: "status",
Key: gui.getKey(config.Universal.Edit), Key: gui.getKey(config.Universal.Edit),
@ -788,13 +781,6 @@ func (gui *Gui) GetInitialKeybindings() []*types.Binding {
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.scrollDownSecondary, Handler: gui.scrollDownSecondary,
}, },
{
ViewName: "secondary",
Contexts: []string{string(context.MAIN_NORMAL_CONTEXT_KEY)},
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleMouseDownSecondary,
},
{ {
ViewName: "main", ViewName: "main",
Contexts: []string{string(context.MAIN_NORMAL_CONTEXT_KEY)}, Contexts: []string{string(context.MAIN_NORMAL_CONTEXT_KEY)},
@ -1361,36 +1347,25 @@ func (gui *Gui) GetInitialKeybindings() []*types.Binding {
Guards: guards, Guards: guards,
} }
// global bindings mouseKeybindings := []*gocui.ViewMouseBinding{}
for _, controller := range []types.IController{ for _, c := range gui.allContexts() {
gui.Controllers.Sync, viewName := c.GetViewName()
gui.Controllers.Undo, contextKey := c.GetKey()
gui.Controllers.Global, for _, binding := range c.GetKeybindings(keybindingsOpts) {
} { // TODO: move all mouse keybindings into the mouse keybindings approach below
context := controller.Context() if !gocui.IsMouseKey(binding.Key) && contextKey != context.GLOBAL_CONTEXT_KEY {
viewName := ""
var contextKeys []string
// nil context means global keybinding
if context != nil {
viewName = context.GetViewName()
contextKeys = []string{string(context.GetKey())}
}
for _, binding := range controller.GetKeybindings(keybindingsOpts) {
binding.Contexts = contextKeys
binding.ViewName = viewName
bindings = append(bindings, binding)
}
}
for _, context := range gui.allContexts() {
viewName := context.GetViewName()
contextKey := context.GetKey()
for _, binding := range context.GetKeybindings(keybindingsOpts) {
binding.Contexts = []string{string(contextKey)} binding.Contexts = []string{string(contextKey)}
}
binding.ViewName = viewName binding.ViewName = viewName
bindings = append(bindings, binding) bindings = append(bindings, binding)
} }
for _, binding := range c.GetMouseKeybindings(keybindingsOpts) {
if contextKey != context.GLOBAL_CONTEXT_KEY {
binding.FromContext = string(contextKey)
}
mouseKeybindings = append(mouseKeybindings, binding)
}
} }
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} { for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
@ -1438,7 +1413,7 @@ func (gui *Gui) GetInitialKeybindings() []*types.Binding {
}...) }...)
} }
return bindings return bindings, mouseKeybindings
} }
func (gui *Gui) resetKeybindings() error { func (gui *Gui) resetKeybindings() error {
@ -1446,7 +1421,10 @@ func (gui *Gui) resetKeybindings() error {
bindings := gui.GetCustomCommandKeybindings() bindings := gui.GetCustomCommandKeybindings()
bindings = append(bindings, gui.GetInitialKeybindings()...) bindings, mouseBindings := gui.GetInitialKeybindings()
// prepending because we want to give our custom keybindings precedence over default keybindings
bindings = append(gui.GetCustomCommandKeybindings(), bindings...)
for _, binding := range bindings { for _, binding := range bindings {
if err := gui.SetKeybinding(binding); err != nil { if err := gui.SetKeybinding(binding); err != nil {
@ -1454,6 +1432,12 @@ func (gui *Gui) resetKeybindings() error {
} }
} }
for _, binding := range mouseBindings {
if err := gui.SetMouseKeybinding(binding); err != nil {
return err
}
}
for viewName := range gui.State.Contexts.InitialViewTabContextMap() { for viewName := range gui.State.Contexts.InitialViewTabContextMap() {
viewName := viewName viewName := viewName
tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(viewName, tabIndex) } tabClickCallback := func(tabIndex int) error { return gui.onViewTabClick(viewName, tabIndex) }
@ -1474,7 +1458,8 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
func (gui *Gui) SetKeybinding(binding *types.Binding) error { func (gui *Gui) SetKeybinding(binding *types.Binding) error {
handler := binding.Handler handler := binding.Handler
if isMouseKey(binding.Key) { // TODO: move all mouse-ey stuff into new mouse approach
if gocui.IsMouseKey(binding.Key) {
handler = func() error { handler = func() error {
// we ignore click events on views that aren't popup panels, when a popup panel is focused // we ignore click events on views that aren't popup panels, when a popup panel is focused
if gui.popupPanelFocused() && gui.currentViewName() != binding.ViewName { if gui.popupPanelFocused() && gui.currentViewName() != binding.ViewName {
@ -1488,19 +1473,18 @@ func (gui *Gui) SetKeybinding(binding *types.Binding) error {
return gui.g.SetKeybinding(binding.ViewName, binding.Contexts, binding.Key, binding.Modifier, gui.wrappedHandler(handler)) return gui.g.SetKeybinding(binding.ViewName, binding.Contexts, binding.Key, binding.Modifier, gui.wrappedHandler(handler))
} }
func isMouseKey(key interface{}) bool { // warning: mutates the binding
switch key { func (gui *Gui) SetMouseKeybinding(binding *gocui.ViewMouseBinding) error {
case baseHandler := binding.Handler
gocui.MouseLeft, newHandler := func(opts gocui.ViewMouseBindingOpts) error {
gocui.MouseRight, // we ignore click events on views that aren't popup panels, when a popup panel is focused
gocui.MouseMiddle, if gui.popupPanelFocused() && gui.currentViewName() != binding.ViewName {
gocui.MouseRelease, return nil
gocui.MouseWheelUp,
gocui.MouseWheelDown,
gocui.MouseWheelLeft,
gocui.MouseWheelRight:
return true
default:
return false
} }
return baseHandler(opts)
}
binding.Handler = newHandler
return gui.g.SetViewClickBinding(binding)
} }

View File

@ -2,7 +2,6 @@ package gui
import ( import (
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme" "github.com/jesseduffield/lazygit/pkg/theme"
) )
@ -262,8 +261,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
continue continue
} }
// ignore contexts whose view is owned by another context right now if !gui.isContextVisible(listContext) {
if types.ContextKey(view.Context) != listContext.GetKey() {
continue continue
} }
@ -300,8 +298,6 @@ func (gui *Gui) prepareView(viewName string) (*gocui.View, error) {
} }
func (gui *Gui) onInitialViewsCreationForRepo() error { func (gui *Gui) onInitialViewsCreationForRepo() error {
gui.setInitialViewContexts()
// hide any popup views. This only applies when we've just switched repos // hide any popup views. This only applies when we've just switched repos
for _, viewName := range gui.popupViewNames() { for _, viewName := range gui.popupViewNames() {
view, err := gui.g.View(viewName) view, err := gui.g.View(viewName)

View File

@ -3,31 +3,28 @@ package gui
import ( import (
"strings" "strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) getBindings(v *gocui.View) []*types.Binding { func (gui *Gui) getBindings(context types.Context) []*types.Binding {
var ( var (
bindingsGlobal, bindingsPanel []*types.Binding bindingsGlobal, bindingsPanel []*types.Binding
) )
bindings := append(gui.GetCustomCommandKeybindings(), gui.GetInitialKeybindings()...) bindings, _ := gui.GetInitialKeybindings()
bindings = append(gui.GetCustomCommandKeybindings(), bindings...)
for _, binding := range bindings { for _, binding := range bindings {
if GetKeyDisplay(binding.Key) != "" && binding.Description != "" { if GetKeyDisplay(binding.Key) != "" && binding.Description != "" {
switch binding.ViewName { if len(binding.Contexts) == 0 {
case "":
bindingsGlobal = append(bindingsGlobal, binding) bindingsGlobal = append(bindingsGlobal, binding)
case v.Name(): } else if utils.IncludesString(binding.Contexts, string(context.GetKey())) {
if len(binding.Contexts) == 0 || utils.IncludesString(binding.Contexts, v.Context) {
bindingsPanel = append(bindingsPanel, binding) bindingsPanel = append(bindingsPanel, binding)
} }
} }
} }
}
// append dummy element to have a separator between // append dummy element to have a separator between
// panel and global keybindings // panel and global keybindings
@ -48,12 +45,8 @@ func opensMenuStyle(str string) string {
} }
func (gui *Gui) handleCreateOptionsMenu() error { func (gui *Gui) handleCreateOptionsMenu() error {
view := gui.g.CurrentView() context := gui.currentContext()
if view == nil { bindings := gui.getBindings(context)
return nil
}
bindings := gui.getBindings(view)
menuItems := make([]*types.MenuItem, len(bindings)) menuItems := make([]*types.MenuItem, len(bindings))

View File

@ -318,7 +318,7 @@ func (gui *Gui) refreshFilesAndSubmodules() error {
gui.c.Log.Error(err) gui.c.Log.Error(err)
} }
if types.ContextKey(gui.Views.Files.Context) == context.FILES_CONTEXT_KEY { if gui.isContextVisible(gui.State.Contexts.Files) {
// doing this a little custom (as opposed to using gui.c.PostRefreshUpdate) because we handle selecting the file explicitly below // doing this a little custom (as opposed to using gui.c.PostRefreshUpdate) because we handle selecting the file explicitly below
if err := gui.State.Contexts.Files.HandleRender(); err != nil { if err := gui.State.Contexts.Files.HandleRender(); err != nil {
return err return err
@ -503,7 +503,15 @@ func (gui *Gui) refreshRemotes() error {
} }
} }
return gui.c.PostRefreshUpdate(gui.mustContextForContextKey(types.ContextKey(gui.Views.Branches.Context))) if err := gui.c.PostRefreshUpdate(gui.State.Contexts.Remotes); err != nil {
return err
}
if err := gui.c.PostRefreshUpdate(gui.State.Contexts.RemoteBranches); err != nil {
return err
}
return nil
} }
func (gui *Gui) refreshStashEntries() error { func (gui *Gui) refreshStashEntries() error {

View File

@ -1,6 +1,9 @@
package types package types
import "github.com/jesseduffield/lazygit/pkg/config" import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
)
type ContextKind int type ContextKind int
@ -10,6 +13,8 @@ const (
TEMPORARY_POPUP TEMPORARY_POPUP
PERSISTENT_POPUP PERSISTENT_POPUP
EXTRAS_CONTEXT EXTRAS_CONTEXT
// only used by the one global context
GLOBAL_CONTEXT
) )
type ParentContexter interface { type ParentContexter interface {
@ -31,6 +36,8 @@ type IBaseContext interface {
GetKeybindings(opts KeybindingsOpts) []*Binding GetKeybindings(opts KeybindingsOpts) []*Binding
AddKeybindingsFn(KeybindingsFn) AddKeybindingsFn(KeybindingsFn)
GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding
AddMouseKeybindingsFn(MouseKeybindingsFn)
} }
type Context interface { type Context interface {
@ -56,9 +63,11 @@ type KeybindingsOpts struct {
} }
type KeybindingsFn func(opts KeybindingsOpts) []*Binding type KeybindingsFn func(opts KeybindingsOpts) []*Binding
type MouseKeybindingsFn func(opts KeybindingsOpts) []*gocui.ViewMouseBinding
type HasKeybindings interface { type HasKeybindings interface {
GetKeybindings(opts KeybindingsOpts) []*Binding GetKeybindings(opts KeybindingsOpts) []*Binding
GetMouseKeybindings(opts KeybindingsOpts) []*gocui.ViewMouseBinding
} }
type IController interface { type IController interface {

View File

@ -1,6 +1,8 @@
package gui package gui
import "github.com/jesseduffield/gocui" import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
// A window refers to a place on the screen which can hold one or more views. // A window refers to a place on the screen which can hold one or more views.
// A view is a box that renders content, and within a window only one view will // A view is a box that renders content, and within a window only one view will
@ -17,28 +19,21 @@ func (gui *Gui) getViewNameForWindow(window string) string {
return viewName return viewName
} }
func (gui *Gui) getWindowForView(view *gocui.View) string { // for now all we actually care about is the context's view so we're storing that
if view == gui.Views.CommitFiles { func (gui *Gui) setWindowContext(c types.Context) {
return gui.State.Contexts.CommitFiles.GetWindowName()
}
return view.Name()
}
func (gui *Gui) setViewAsActiveForWindow(view *gocui.View) {
if gui.State.WindowViewNameMap == nil { if gui.State.WindowViewNameMap == nil {
gui.State.WindowViewNameMap = map[string]string{} gui.State.WindowViewNameMap = map[string]string{}
} }
gui.State.WindowViewNameMap[gui.getWindowForView(view)] = view.Name() gui.State.WindowViewNameMap[c.GetWindowName()] = c.GetViewName()
} }
func (gui *Gui) currentWindow() string { func (gui *Gui) currentWindow() string {
return gui.getWindowForView(gui.g.CurrentView()) return gui.currentContext().GetWindowName()
} }
func (gui *Gui) resetWindowForView(view *gocui.View) { func (gui *Gui) resetWindowContext(c types.Context) {
window := gui.getWindowForView(view)
// we assume here that the window contains as its default view a view with the same name as the window // we assume here that the window contains as its default view a view with the same name as the window
gui.State.WindowViewNameMap[window] = window windowName := c.GetWindowName()
gui.State.WindowViewNameMap[windowName] = windowName
} }

View File

@ -69,6 +69,30 @@ type tabClickBinding struct {
handler tabClickHandler handler tabClickHandler
} }
type ViewMouseBinding struct {
// the view that is clicked
ViewName string
// the context we are in when the click occurs. Not necessarily the context
// of the view we're clicking. If this is blank then it is a global binding.
FromContext string
Handler func(ViewMouseBindingOpts) error
// must be a mouse key
Key Key
}
type ViewMouseBindingOpts struct {
// cursor x/y
Cx int
Cy int
// origin x/y
Ox int
Oy int
}
type GuiMutexes struct { type GuiMutexes struct {
// tickingMutex ensures we don't have two loops ticking. The point of 'ticking' // tickingMutex ensures we don't have two loops ticking. The point of 'ticking'
// is to refresh the gui rapidly so that loader characters can be animated. // is to refresh the gui rapidly so that loader characters can be animated.
@ -111,6 +135,7 @@ type Gui struct {
StartTime time.Time StartTime time.Time
tabClickBindings []*tabClickBinding tabClickBindings []*tabClickBinding
viewMouseBindings []*ViewMouseBinding
gEvents chan GocuiEvent gEvents chan GocuiEvent
userEvents chan userEvent userEvents chan userEvent
views []*View views []*View
@ -162,6 +187,8 @@ type Gui struct {
screen tcell.Screen screen tcell.Screen
suspendedMutex sync.Mutex suspendedMutex sync.Mutex
suspended bool suspended bool
currentContext string
} }
// NewGui returns a new Gui object with a given output mode. // NewGui returns a new Gui object with a given output mode.
@ -237,6 +264,10 @@ func (g *Gui) Size() (x, y int) {
return g.maxX, g.maxY return g.maxX, g.maxY
} }
func (g *Gui) SetCurrentContext(context string) {
g.currentContext = context
}
// SetRune writes a rune at the given point, relative to the top-left // SetRune writes a rune at the given point, relative to the top-left
// corner of the terminal. It checks if the position is valid and applies // corner of the terminal. It checks if the position is valid and applies
// the given colors. // the given colors.
@ -472,6 +503,7 @@ func (g *Gui) DeleteKeybinding(viewname string, key interface{}, mod Modifier) e
func (g *Gui) DeleteAllKeybindings() { func (g *Gui) DeleteAllKeybindings() {
g.keybindings = []*keybinding{} g.keybindings = []*keybinding{}
g.tabClickBindings = []*tabClickBinding{} g.tabClickBindings = []*tabClickBinding{}
g.viewMouseBindings = []*ViewMouseBinding{}
} }
// DeleteKeybindings deletes all keybindings of view. // DeleteKeybindings deletes all keybindings of view.
@ -495,6 +527,12 @@ func (g *Gui) SetTabClickBinding(viewName string, handler tabClickHandler) error
return nil return nil
} }
func (g *Gui) SetViewClickBinding(binding *ViewMouseBinding) error {
g.viewMouseBindings = append(g.viewMouseBindings, binding)
return nil
}
// BlackListKeybinding adds a keybinding to the blacklist // BlackListKeybinding adds a keybinding to the blacklist
func (g *Gui) BlacklistKeybinding(k Key) error { func (g *Gui) BlacklistKeybinding(k Key) error {
for _, j := range g.blacklist { for _, j := range g.blacklist {
@ -1098,6 +1136,17 @@ func (g *Gui) onKey(ev *GocuiEvent) error {
return err return err
} }
if ev.Mod == ModNone && IsMouseKey(ev.Key) {
opts := ViewMouseBindingOpts{Cx: newCx, Cy: newCy, Ox: v.ox, Oy: v.oy}
matched, err := g.execMouseKeybindings(v.Name(), ev, opts)
if err != nil {
return err
}
if matched {
return nil
}
}
if _, err := g.execKeybindings(v, ev); err != nil { if _, err := g.execKeybindings(v, ev); err != nil {
return err return err
} }
@ -1106,6 +1155,40 @@ func (g *Gui) onKey(ev *GocuiEvent) error {
return nil return nil
} }
func (g *Gui) execMouseKeybindings(viewName string, ev *GocuiEvent, opts ViewMouseBindingOpts) (bool, error) {
// first pass looks for ones that match both the view and the current context
for _, binding := range g.viewMouseBindings {
if binding.ViewName == viewName && binding.FromContext == g.currentContext && ev.Key == binding.Key {
return true, binding.Handler(opts)
}
}
for _, binding := range g.viewMouseBindings {
if binding.ViewName == viewName && ev.Key == binding.Key {
return true, binding.Handler(opts)
}
}
return false, nil
}
func IsMouseKey(key interface{}) bool {
switch key {
case
MouseLeft,
MouseRight,
MouseMiddle,
MouseRelease,
MouseWheelUp,
MouseWheelDown,
MouseWheelLeft,
MouseWheelRight:
return true
default:
return false
}
}
// execKeybindings executes the keybinding handlers that match the passed view // execKeybindings executes the keybinding handlers that match the passed view
// and event. The value of matched is true if there is a match and no errors. // and event. The value of matched is true if there is a match and no errors.
func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) (matched bool, err error) { func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) (matched bool, err error) {
@ -1136,10 +1219,10 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) (matched bool, err error)
if !kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) { if !kb.matchKeypress(Key(ev.Key), ev.Ch, Modifier(ev.Mod)) {
continue continue
} }
if kb.matchView(v) { if g.matchView(v, kb) {
return g.execKeybinding(v, kb) return g.execKeybinding(v, kb)
} }
if v != nil && kb.matchView(v.ParentView) { if v != nil && g.matchView(v.ParentView, kb) {
matchingParentViewKb = kb matchingParentViewKb = kb
} }
if globalKb == nil && kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) { if globalKb == nil && kb.viewName == "" && ((v != nil && !v.Editable) || (kb.ch == 0 && kb.key != KeyCtrlU && kb.key != KeyCtrlA && kb.key != KeyCtrlE)) {
@ -1340,3 +1423,27 @@ func (g *Gui) Resume() error {
return g.screen.Resume() return g.screen.Resume()
} }
// matchView returns if the keybinding matches the current 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 == true && kb.ch != 0 {
return false
}
if kb.viewName != v.name {
return false
}
// if the keybinding doesn't specify contexts, it applies for all contexts
if len(kb.contexts) == 0 {
return true
}
for _, context := range kb.contexts {
if context == g.currentContext {
return true
}
}
return false
}

View File

@ -124,30 +124,6 @@ func (kb *keybinding) matchKeypress(key Key, ch rune, mod Modifier) bool {
return kb.key == key && kb.ch == ch && kb.mod == mod return kb.key == key && kb.ch == ch && kb.mod == mod
} }
// matchView returns if the keybinding matches the current view (and the view's context)
func (kb *keybinding) matchView(v *View) bool {
// if the user is typing in a field, ignore char keys
if v == nil {
return false
}
if v.Editable == true && kb.ch != 0 {
return false
}
if kb.viewName != v.name {
return false
}
// if the keybinding doesn't specify contexts, it applies for all contexts
if len(kb.contexts) == 0 {
return true
}
for _, context := range kb.contexts {
if context == v.Context {
return true
}
}
return false
}
// translations for strings to keys // translations for strings to keys
var translate = map[string]Key{ var translate = map[string]Key{
"F1": KeyF1, "F1": KeyF1,

View File

@ -149,8 +149,6 @@ type View struct {
// ParentView is the view which catches events bubbled up from the given view if there's no matching handler // ParentView is the view which catches events bubbled up from the given view if there's no matching handler
ParentView *View ParentView *View
Context string // this is for assigning keybindings to a view only in certain contexts
searcher *searcher searcher *searcher
// KeybindOnEdit should be set to true when you want to execute keybindings even when the view is editable // KeybindOnEdit should be set to true when you want to execute keybindings even when the view is editable