diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2022-02-05 14:42:56 +1100 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-03-17 19:13:40 +1100 |
commit | 482bdc4f1ea5448c5e98697ae66221e544ea40dd (patch) | |
tree | 7df478f9870e3dddc85f77d7b77439bed16e3c7b /pkg | |
parent | 8e3484d8e98faf12f8395eaf5f9e8381f77a8e52 (diff) |
more refactoring
Diffstat (limited to 'pkg')
27 files changed, 249 insertions, 185 deletions
diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go index 15e390356..f499a2f58 100644 --- a/pkg/cheatsheet/generate.go +++ b/pkg/cheatsheet/generate.go @@ -124,7 +124,7 @@ func formatBinding(binding *types.Binding) string { func getBindingSections(mApp *app.App) []*bindingSection { bindingSections := []*bindingSection{} - bindings := mApp.Gui.GetInitialKeybindings() + bindings, _ := mApp.Gui.GetInitialKeybindings() type contextAndViewType struct { subtitle string diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go index f55b3d6c3..05f23374c 100644 --- a/pkg/gui/commit_files_panel.go +++ b/pkg/gui/commit_files_panel.go @@ -257,7 +257,7 @@ func (gui *Gui) handleToggleCommitFileDirCollapsed() 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 // 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.SetRefName(opts.RefName) diff --git a/pkg/gui/context.go b/pkg/gui/context.go index 09b669b04..657274c1f 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -60,6 +60,10 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error { 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() // push onto stack @@ -112,6 +116,8 @@ func (gui *Gui) returnFromContext() error { gui.State.ContextManager.ContextStack = gui.State.ContextManager.ContextStack[:n] + gui.g.SetCurrentContext(string(newContext.GetKey())) + gui.State.ContextManager.Unlock() 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 the current view we trigger a focus; re-selecting the current item. func (gui *Gui) postRefreshUpdate(c types.Context) error { - v, err := gui.g.View(c.GetViewName()) - if err != nil { - return nil - } - - if types.ContextKey(v.Context) != c.GetKey() { + if gui.State.ViewContextMap[c.GetViewName()].GetKey() != c.GetKey() { return nil } @@ -174,19 +175,18 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro if err != nil { 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) + gui.setWindowContext(c) + gui.setViewTabForContext(c) if viewName == "main" { - gui.changeMainViewsContext(c.GetKey()) + gui.changeMainViewsContext(c) } else { - gui.changeMainViewsContext(context.MAIN_NORMAL_CONTEXT_KEY) + gui.changeMainViewsContext(gui.State.Contexts.Normal) } - gui.setViewTabForContext(c) - + gui.g.SetCurrentContext(string(c.GetKey())) if _, err := gui.g.SetCurrentView(viewName); err != nil { 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 @@ -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 func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error { var previousView *gocui.View @@ -364,7 +349,7 @@ func (gui *Gui) onViewFocusLost(oldView *gocui.View, newView *gocui.View) error _ = oldView.SetOriginX(0) 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 { 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 // other views can have their context changed directly but this function helps // keep the main and secondary views in sync -func (gui *Gui) changeMainViewsContext(contextKey types.ContextKey) { - if gui.State.MainContext == contextKey { +func (gui *Gui) changeMainViewsContext(c types.Context) { + if gui.State.MainContext == c.GetKey() { 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: - gui.Views.Main.Context = string(contextKey) - gui.Views.Secondary.Context = string(contextKey) + gui.State.ViewContextMap[gui.Views.Main.Name()] = c + gui.State.ViewContextMap[gui.Views.Secondary.Name()] = c 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 { @@ -452,8 +437,11 @@ func (gui *Gui) contextForContextKey(contextKey types.ContextKey) (types.Context } func (gui *Gui) rerenderView(view *gocui.View) error { - contextKey := types.ContextKey(view.Context) - context := gui.mustContextForContextKey(contextKey) + context, ok := gui.State.ViewContextMap[view.Name()] + + if !ok { + panic("no context set against view " + view.Name()) + } return context.HandleRender() } @@ -467,6 +455,10 @@ func (gui *Gui) getSideContextSelectedItemId() string { 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 // func (gui *Gui) getCurrentSideView() *gocui.View { // currentSideContext := gui.currentSideContext() diff --git a/pkg/gui/context/base_context.go b/pkg/gui/context/base_context.go index c61f57cf4..5dbc3cbf4 100644 --- a/pkg/gui/context/base_context.go +++ b/pkg/gui/context/base_context.go @@ -1,6 +1,7 @@ package context import ( + "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui/types" ) @@ -11,8 +12,8 @@ type BaseContext struct { windowName string onGetOptionsMap func() map[string]string - keybindingsFns []types.KeybindingsFn - keybindings []*types.Binding + keybindingsFns []types.KeybindingsFn + mouseKeybindingsFns []types.MouseKeybindingsFn *ParentContextMgr } @@ -80,3 +81,18 @@ func (self *BaseContext) GetKeybindings(opts types.KeybindingsOpts) []*types.Bin func (self *BaseContext) AddKeybindingsFn(fn types.KeybindingsFn) { 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 +} diff --git a/pkg/gui/context/context.go b/pkg/gui/context/context.go index 20d67a5a7..b45a089be 100644 --- a/pkg/gui/context/context.go +++ b/pkg/gui/context/context.go @@ -3,6 +3,7 @@ package context import "github.com/jesseduffield/lazygit/pkg/gui/types" const ( + GLOBAL_CONTEXT_KEY types.ContextKey = "global" STATUS_CONTEXT_KEY types.ContextKey = "status" FILES_CONTEXT_KEY types.ContextKey = "files" LOCAL_BRANCHES_CONTEXT_KEY types.ContextKey = "localBranches" @@ -29,6 +30,7 @@ const ( ) var AllContextKeys = []types.ContextKey{ + GLOBAL_CONTEXT_KEY, STATUS_CONTEXT_KEY, FILES_CONTEXT_KEY, LOCAL_BRANCHES_CONTEXT_KEY, @@ -55,6 +57,7 @@ var AllContextKeys = []types.ContextKey{ } type ContextTree struct { + Global types.Context Status types.Context Files *WorkingTreeContext Submodules types.IListContext diff --git a/pkg/gui/context_config.go b/pkg/gui/context_config.go index eec6cdd69..4e5c241d0 100644 --- a/pkg/gui/context_config.go +++ b/pkg/gui/context_config.go @@ -7,6 +7,7 @@ import ( func (gui *Gui) allContexts() []types.Context { return []types.Context{ + gui.State.Contexts.Global, gui.State.Contexts.Status, gui.State.Contexts.Files, gui.State.Contexts.Submodules, @@ -34,12 +35,23 @@ func (gui *Gui) allContexts() []types.Context { func (gui *Gui) contextTree() *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( context.NewBaseContext(context.NewBaseContextOpts{ Kind: types.SIDE_CONTEXT, ViewName: "status", - Key: context.STATUS_CONTEXT_KEY, WindowName: "status", + Key: context.STATUS_CONTEXT_KEY, }), NewSimpleContextOpts{ OnRenderToMain: OnFocusWrapper(gui.statusRenderToMain), diff --git a/pkg/gui/controllers/attach.go b/pkg/gui/controllers/attach.go new file mode 100644 index 000000000..008c15505 --- /dev/null +++ b/pkg/gui/controllers/attach.go @@ -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) + } +} diff --git a/pkg/gui/controllers/base_controller.go b/pkg/gui/controllers/base_controller.go new file mode 100644 index 000000000..e510c1a9f --- /dev/null +++ b/pkg/gui/controllers/base_controller.go @@ -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 +} diff --git a/pkg/gui/controllers/bisect_controller.go b/pkg/gui/controllers/bisect_controller.go index 9841b5e59..58c8a6db7 100644 --- a/pkg/gui/controllers/bisect_controller.go +++ b/pkg/gui/controllers/bisect_controller.go @@ -11,6 +11,8 @@ import ( ) type BisectController struct { + baseController + c *types.ControllerCommon context types.IListContext git *commands.GitCommand @@ -32,10 +34,11 @@ func NewBisectController( getCommits func() []*models.Commit, ) *BisectController { return &BisectController{ - c: c, - context: context, - git: git, - bisectHelper: bisectHelper, + baseController: baseController{}, + c: c, + context: context, + git: git, + bisectHelper: bisectHelper, getSelectedLocalCommit: getSelectedLocalCommit, getCommits: getCommits, diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index c8af30e62..d853ba731 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -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 { if node.IsLeaf() { 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}) +} diff --git a/pkg/gui/controllers/global_controller.go b/pkg/gui/controllers/global_controller.go index 3560a0412..dd0c8ea3b 100644 --- a/pkg/gui/controllers/global_controller.go +++ b/pkg/gui/controllers/global_controller.go @@ -7,6 +7,8 @@ import ( ) type GlobalController struct { + baseController + c *types.ControllerCommon os *oscommands.OSCommand } @@ -16,8 +18,9 @@ func NewGlobalController( os *oscommands.OSCommand, ) *GlobalController { return &GlobalController{ - c: c, - os: os, + baseController: baseController{}, + c: c, + os: os, } } diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 5458697e3..e962ea48e 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -22,6 +22,7 @@ type ( ) type LocalCommitsController struct { + baseController c *types.ControllerCommon context types.IListContext os *oscommands.OSCommand @@ -68,6 +69,7 @@ func NewLocalCommitsController( setShowWholeGitGraph func(bool), ) *LocalCommitsController { return &LocalCommitsController{ + baseController: baseController{}, c: c, context: context, os: os, diff --git a/pkg/gui/controllers/menu_controller.go b/pkg/gui/controllers/menu_controller.go index 7c93ef6f7..cbd24e188 100644 --- a/pkg/gui/controllers/menu_controller.go +++ b/pkg/gui/controllers/menu_controller.go @@ -6,6 +6,8 @@ import ( ) type MenuController struct { + baseController + c *types.ControllerCommon context types.IListContext @@ -20,6 +22,8 @@ func NewMenuController( getSelectedMenuItem func() *types.MenuItem, ) *MenuController { return &MenuController{ + baseController: baseController{}, + c: c, context: context, getSelectedMenuItem: getSelectedMenuItem, diff --git a/pkg/gui/controllers/remotes_controller.go b/pkg/gui/controllers/remotes_controller.go index 9f7acb9d0..73b1c57ab 100644 --- a/pkg/gui/controllers/remotes_controller.go +++ b/pkg/gui/controllers/remotes_controller.go @@ -10,6 +10,8 @@ import ( ) type RemotesController struct { + baseController + c *types.ControllerCommon context types.IListContext git *commands.GitCommand @@ -30,6 +32,7 @@ func NewRemotesController( setRemoteBranches func([]*models.RemoteBranch), ) *RemotesController { return &RemotesController{ + baseController: baseController{}, c: c, git: git, contexts: contexts, diff --git a/pkg/gui/controllers/submodules_controller.go b/pkg/gui/controllers/submodules_controller.go index f1ac7acf3..1db27f6e6 100644 --- a/pkg/gui/controllers/submodules_controller.go +++ b/pkg/gui/controllers/submodules_controller.go @@ -13,6 +13,8 @@ import ( ) type SubmodulesController struct { + baseController + c *types.ControllerCommon context types.IListContext git *commands.GitCommand @@ -31,6 +33,7 @@ func NewSubmodulesController( getSelectedSubmodule func() *models.SubmoduleConfig, ) *SubmodulesController { return &SubmodulesController{ + baseController: baseController{}, c: c, context: context, git: git, diff --git a/pkg/gui/controllers/sync_controller.go b/pkg/gui/controllers/sync_controller.go index 106b4c516..f3f2894b0 100644 --- a/pkg/gui/controllers/sync_controller.go +++ b/pkg/gui/controllers/sync_controller.go @@ -11,10 +11,8 @@ import ( ) type SyncController struct { - // I've said publicly that I'm against single-letter variable names but in this - // 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 + baseController + c *types.ControllerCommon git *commands.GitCommand @@ -35,8 +33,9 @@ func NewSyncController( CheckMergeOrRebase func(error) error, ) *SyncController { return &SyncController{ - c: c, - git: git, + baseController: baseController{}, + c: c, + git: git, getCheckedOutBranch: getCheckedOutBranch, suggestionsHelper: suggestionsHelper, diff --git a/pkg/gui/controllers/tags_controller.go b/pkg/gui/controllers/tags_controller.go index 231f12736..508820061 100644 --- a/pkg/gui/controllers/tags_controller.go +++ b/pkg/gui/controllers/tags_controller.go @@ -9,6 +9,8 @@ import ( ) type TagsController struct { + baseController + c *types.ControllerCommon context *context.TagsContext git *commands.GitCommand @@ -35,6 +37,7 @@ func NewTagsController( switchToSubCommitsContext func(string) error, ) *TagsController { return &TagsController{ + baseController: baseController{}, c: c, context: context, git: git, diff --git a/pkg/gui/controllers/undo_controller.go b/pkg/gui/controllers/undo_controller.go index 0f288957c..683fb2b84 100644 --- a/pkg/gui/controllers/undo_controller.go +++ b/pkg/gui/controllers/undo_controller.go @@ -19,6 +19,8 @@ import ( // two user actions, meaning we end up undoing reflog entry C. Redoing works in a similar way. type UndoController struct { + baseController + c *types.ControllerCommon git *commands.GitCommand @@ -39,6 +41,7 @@ func NewUndoController( getFilteredReflogCommits func() []*models.Commit, ) *UndoController { return &UndoController{ + baseController: baseController{}, c: c, git: git, refsHelper: refsHelper, diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go index 04f7efb61..ba8a0a237 100644 --- a/pkg/gui/global_handlers.go +++ b/pkg/gui/global_handlers.go @@ -182,11 +182,6 @@ func (gui *Gui) handleRefresh() error { func (gui *Gui) handleMouseDownMain() error { 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: return gui.enterCommitFile(types.OnFocusOpts{ClickedViewName: "main", ClickedViewLineIdx: gui.Views.Main.SelectedLineIdx()}) } @@ -194,15 +189,6 @@ func (gui *Gui) handleMouseDownMain() error { 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) { gui.c.LogAction("Fetch") err = gui.git.Sync.Fetch(git_commands.FetchOptions{}) diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 2b4fe66ca..ccb032175 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -590,6 +590,15 @@ func (gui *Gui) resetControllers() { 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{ Submodules: submodulesController, Global: controllers.NewGlobalController( @@ -660,14 +669,6 @@ func (gui *Gui) resetControllers() { gui.State.Contexts.Menu, 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( controllerCommon, gui.git, @@ -678,17 +679,13 @@ func (gui *Gui) resetControllers() { Sync: syncController, } - gui.State.Contexts.Submodules.AddKeybindingsFn(gui.Controllers.Submodules.GetKeybindings) - gui.Controllers.Files.Attach(gui.State.Contexts.Files) - gui.State.Contexts.Files.AddKeybindingsFn(gui.Controllers.Files.GetKeybindings) - gui.State.Contexts.Tags.AddKeybindingsFn(gui.Controllers.Tags.GetKeybindings) - // TODO: commit to one name here: local commits or branch commits - gui.State.Contexts.BranchCommits.AddKeybindingsFn(gui.Controllers.LocalCommits.GetKeybindings) - gui.State.Contexts.BranchCommits.AddKeybindingsFn(gui.Controllers.Bisect.GetKeybindings) - 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 + controllers.AttachControllers(gui.State.Contexts.Files, gui.Controllers.Files) + controllers.AttachControllers(gui.State.Contexts.Tags, gui.Controllers.Tags) |