summaryrefslogtreecommitdiffstats
path: root/pkg/gui/context.go
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-06-13 11:01:26 +1000
committerJesse Duffield <jessedduffield@gmail.com>2022-08-06 13:49:11 +1000
commit524bf83a4a681408c3fb57818f6968cab632e0ae (patch)
tree8858b4ee8d4670dcdd1637fe5fedf00ff080c154 /pkg/gui/context.go
parent6dfef08efc5c7f262194c0af35fd777428f33a1a (diff)
refactor to only have one context per view
Diffstat (limited to 'pkg/gui/context.go')
-rw-r--r--pkg/gui/context.go192
1 files changed, 46 insertions, 146 deletions
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
index f08acec06..e4719ab13 100644
--- a/pkg/gui/context.go
+++ b/pkg/gui/context.go
@@ -1,8 +1,6 @@
package gui
import (
- "errors"
- "fmt"
"sort"
"strings"
@@ -28,24 +26,6 @@ func (gui *Gui) popupViewNames() []string {
})
}
-func (gui *Gui) currentContextKeyIgnoringPopups() types.ContextKey {
- gui.State.ContextManager.RLock()
- defer gui.State.ContextManager.RUnlock()
-
- stack := gui.State.ContextManager.ContextStack
-
- for i := range stack {
- reversedIndex := len(stack) - 1 - i
- context := stack[reversedIndex]
- kind := stack[reversedIndex].GetKind()
- if kind != types.TEMPORARY_POPUP && kind != types.PERSISTENT_POPUP {
- return context.GetKey()
- }
- }
-
- return ""
-}
-
// use replaceContext when you don't want to return to the original context upon
// hitting escape: you want to go that context's parent instead.
func (gui *Gui) replaceContext(c types.Context) error {
@@ -64,31 +44,43 @@ func (gui *Gui) replaceContext(c types.Context) error {
defer gui.State.ContextManager.Unlock()
- return gui.activateContext(c)
+ return gui.activateContext(c, types.OnFocusOpts{})
}
-func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
- // using triple dot but you should only ever pass one of these opt structs
- if len(opts) > 1 {
- return errors.New("cannot pass multiple opts to pushContext")
- }
-
+func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
if !c.IsFocusable() {
return nil
}
+ contextsToDeactivate := gui.pushToContextStack(c)
+
+ for _, contextToDeactivate := range contextsToDeactivate {
+ if err := gui.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
+ return err
+ }
+ }
+
+ return gui.activateContext(c, opts)
+}
+
+// Adjusts the context stack based on the context that's being pushed and returns contexts to deactivate
+func (gui *Gui) pushToContextStack(c types.Context) []types.Context {
+ contextsToDeactivate := []types.Context{}
+
gui.State.ContextManager.Lock()
+ defer gui.State.ContextManager.Unlock()
if len(gui.State.ContextManager.ContextStack) == 0 {
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
} else if c.GetKind() == types.SIDE_CONTEXT {
// if we are switching to a side context, remove all other contexts in the stack
+ contextsToDeactivate = gui.State.ContextManager.ContextStack
+ gui.State.ContextManager.ContextStack = []types.Context{c}
+ } else if c.GetKind() == types.MAIN_CONTEXT {
+ // if we're switching to a main context, remove all other main contexts in the stack
for _, stackContext := range gui.State.ContextManager.ContextStack {
- if stackContext.GetKey() != c.GetKey() {
- if err := gui.deactivateContext(stackContext); err != nil {
- gui.State.ContextManager.Unlock()
- return err
- }
+ if stackContext.GetKind() == types.MAIN_CONTEXT {
+ contextsToDeactivate = append(contextsToDeactivate, stackContext)
}
}
gui.State.ContextManager.ContextStack = []types.Context{c}
@@ -101,12 +93,11 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
// escape back to previous temporary popups, but because we're currently reusing
// views for this, you might not be able to get back to where you previously were.
// The exception is when going to the search context e.g. for searching a menu.
- if topContext.GetKind() == types.TEMPORARY_POPUP && c.GetKey() != context.SEARCH_CONTEXT_KEY {
- if err := gui.deactivateContext(topContext); err != nil {
- gui.State.ContextManager.Unlock()
- return err
- }
+ if (topContext.GetKind() == types.TEMPORARY_POPUP && c.GetKey() != context.SEARCH_CONTEXT_KEY) ||
+ // we only ever want one main context on the stack at a time.
+ (topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
+ contextsToDeactivate = append(contextsToDeactivate, topContext)
_, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
}
@@ -114,16 +105,7 @@ func (gui *Gui) pushContext(c types.Context, opts ...types.OnFocusOpts) error {
}
}
- gui.State.ContextManager.Unlock()
-
- return gui.activateContext(c, opts...)
-}
-
-// pushContextWithView is to be used when you don't know which context you
-// want to switch to: you only know the view that you want to switch to. It will
-// look up the context currently active for that view and switch to that context
-func (gui *Gui) pushContextWithView(viewName string) error {
- return gui.c.PushContext(gui.State.ViewContextMap.Get(viewName))
+ return contextsToDeactivate
}
func (gui *Gui) popContext() error {
@@ -140,18 +122,16 @@ func (gui *Gui) popContext() error {
newContext := gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
- gui.g.SetCurrentContext(string(newContext.GetKey()))
-
gui.State.ContextManager.Unlock()
- if err := gui.deactivateContext(currentContext); err != nil {
+ if err := gui.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
return err
}
- return gui.activateContext(newContext)
+ return gui.activateContext(newContext, types.OnFocusOpts{})
}
-func (gui *Gui) deactivateContext(c types.Context) error {
+func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
view, _ := gui.g.View(c.GetViewName())
if view != nil && view.IsSearching() {
@@ -167,7 +147,7 @@ func (gui *Gui) deactivateContext(c types.Context) error {
view.Visible = false
}
- if err := c.HandleFocusLost(); err != nil {
+ if err := c.HandleFocusLost(opts); err != nil {
return err
}
@@ -178,16 +158,12 @@ 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 {
- if gui.State.ViewContextMap.Get(c.GetViewName()).GetKey() != c.GetKey() {
- return nil
- }
-
if err := c.HandleRender(); err != nil {
return err
}
if gui.currentViewName() == c.GetViewName() {
- if err := c.HandleFocus(); err != nil {
+ if err := c.HandleFocus(types.OnFocusOpts{}); err != nil {
return err
}
}
@@ -195,29 +171,16 @@ func (gui *Gui) postRefreshUpdate(c types.Context) error {
return nil
}
-func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) error {
+func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
viewName := c.GetViewName()
v, err := gui.g.View(viewName)
if err != nil {
return err
}
- originalViewContext := gui.State.ViewContextMap.Get(viewName)
- var originalViewContextKey types.ContextKey = ""
- if originalViewContext != nil {
- originalViewContextKey = originalViewContext.GetKey()
- }
-
gui.setWindowContext(c)
- gui.setViewTabForContext(c)
- if viewName == "main" {
- gui.changeMainViewsContext(c)
- } else {
- gui.changeMainViewsContext(gui.State.Contexts.Normal)
- }
-
- gui.g.SetCurrentContext(string(c.GetKey()))
+ gui.moveToTopOfWindow(c)
if _, err := gui.g.SetCurrentView(viewName); err != nil {
return err
}
@@ -229,15 +192,6 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
v.Visible = true
- // if the new context's view was previously displaying another context, render the new context
- if originalViewContextKey != c.GetKey() {
- if err := c.HandleRender(); err != nil {
- return err
- }
- }
-
- gui.ViewContextMapSet(viewName, c)
-
gui.g.Cursor = v.Editable
// render the options available for the current context at the bottom of the screen
@@ -247,7 +201,7 @@ func (gui *Gui) activateContext(c types.Context, opts ...types.OnFocusOpts) erro
}
gui.renderOptionsMap(optionsMap)
- if err := c.HandleFocus(opts...); err != nil {
+ if err := c.HandleFocus(opts); err != nil {
return err
}
@@ -266,16 +220,6 @@ func (gui *Gui) renderOptionsMap(optionsMap map[string]string) {
_ = gui.renderString(gui.Views.Options, gui.optionsMapToString(optionsMap))
}
-// also setting context on view for now. We'll need to pick one of these two approaches to stick with.
-func (gui *Gui) ViewContextMapSet(viewName string, c types.Context) {
- gui.State.ViewContextMap.Set(viewName, c)
- view, err := gui.g.View(viewName)
- if err != nil {
- panic(err)
- }
- view.Context = string(c.GetKey())
-}
-
// // currently unused
// func (gui *Gui) renderContextStack() string {
// result := ""
@@ -339,6 +283,10 @@ func (gui *Gui) currentStaticContext() types.Context {
gui.State.ContextManager.RLock()
defer gui.State.ContextManager.RUnlock()
+ return gui.currentStaticContextWithoutLock()
+}
+
+func (gui *Gui) currentStaticContextWithoutLock() types.Context {
stack := gui.State.ContextManager.ContextStack
if len(stack) == 0 {
@@ -400,57 +348,14 @@ func (gui *Gui) TransientContexts() []types.Context {
})
}
-// changeContext is a helper function for when we want to change a 'main' context
-// 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(c types.Context) {
- if gui.State.MainContext == c.GetKey() {
- return
- }
-
- 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.ViewContextMapSet(gui.Views.Main.Name(), c)
- gui.ViewContextMapSet(gui.Views.Secondary.Name(), c)
- default:
- panic(fmt.Sprintf("unknown context for main: %s", c.GetKey()))
- }
-
- gui.State.MainContext = c.GetKey()
-}
-
-func (gui *Gui) viewTabNames(viewName string) []string {
- tabContexts := gui.State.ViewTabContextMap[viewName]
-
- return slices.Map(tabContexts, func(tabContext context.TabContext) string {
- return tabContext.Tab
- })
-}
-
-func (gui *Gui) setViewTabForContext(c types.Context) {
- // search for the context in our map and if we find it, set the tab for the corresponding view
- tabContexts, ok := gui.State.ViewTabContextMap[c.GetViewName()]
+func (gui *Gui) rerenderView(view *gocui.View) error {
+ context, ok := gui.contextForView(view.Name())
if !ok {
- return
- }
-
- for tabIndex, tabContext := range tabContexts {
- if tabContext.Context.GetKey() == c.GetKey() {
- // get the view, set the tab
- v, err := gui.g.View(c.GetViewName())
- if err != nil {
- gui.c.Log.Error(err)
- return
- }
- v.TabIndex = tabIndex
- return
- }
+ gui.Log.Errorf("no context found for view %s", view.Name())
+ return nil
}
-}
-func (gui *Gui) rerenderView(view *gocui.View) error {
- return gui.State.ViewContextMap.Get(view.Name()).HandleRender()
+ return context.HandleRender()
}
func (gui *Gui) getSideContextSelectedItemId() string {
@@ -462,11 +367,6 @@ 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.Get(c.GetViewName()).GetKey() == c.GetKey()
-}
-
// currently unused
// func (gui *Gui) getCurrentSideView() *gocui.View {
// currentSideContext := gui.currentSideContext()