diff --git a/pkg/gui/context.go b/pkg/gui/context.go index cd959274c..d83b144f2 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -3,6 +3,7 @@ package gui import ( "sync" + "github.com/jesseduffield/generics/set" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" @@ -180,10 +181,6 @@ func (self *ContextMgr) Activate(c types.Context, opts types.OnFocusOpts) { self.gui.helpers.Window.MoveToTopOfWindow(c) inputViewName := c.GetInputViewName() - oldView := self.gui.c.GocuiGui().CurrentView() - if oldView != nil && oldView.Name() != inputViewName { - oldView.HighlightInactive = true - } if _, err := self.gui.c.GocuiGui().SetCurrentView(inputViewName); err != nil { panic(err) } @@ -199,9 +196,37 @@ func (self *ContextMgr) Activate(c types.Context, opts types.OnFocusOpts) { self.gui.c.GocuiGui().Cursor = v.Editable && v.Mask == "" + self.updateSelectionHighlights() + c.HandleFocus(opts) } +// updateSelectionHighlights re-derives which views draw a selection, and which of +// them draw theirs as the active one: a view shows a selection while its context is +// on the stack and has something to select, and the context the user is in shows the +// active selection while the ones behind it show inactive ones. +// +// Both of those can change, so this is called wherever they do: from Activate, which +// every change to the stack goes through; after a refresh, since that is when the +// contents of a list change; and from whoever tells a context that its content has +// gained or lost something to select. +func (self *ContextMgr) updateSelectionHighlights() { + self.RLock() + defer self.RUnlock() + + onStack := set.NewFromSlice(lo.Map(self.ContextStack, + func(c types.Context, _ int) types.ContextKey { return c.GetKey() })) + currentKey := self.currentContextWithoutLock().GetKey() + + for _, c := range self.allContexts.Flatten() { + // The global context has no view of its own. + if view := c.GetView(); view != nil { + view.Highlight = onStack.Includes(c.GetKey()) && c.HasSelectableContent() + view.HighlightInactive = c.GetKey() != currentKey + } + } +} + func (self *ContextMgr) Current() types.Context { self.RLock() defer self.RUnlock() diff --git a/pkg/gui/context/list_context_trait.go b/pkg/gui/context/list_context_trait.go index 102684e58..2e4ae7267 100644 --- a/pkg/gui/context/list_context_trait.go +++ b/pkg/gui/context/list_context_trait.go @@ -106,8 +106,6 @@ func formatListFooter(selectedLineIdx int, length int) string { func (self *ListContextTrait) HandleFocus(opts types.OnFocusOpts) { self.FocusLine(!opts.KeepScrollPosition) - self.GetViewTrait().SetHighlight(self.HasSelectableContent()) - self.Context.HandleFocus(opts) } diff --git a/pkg/gui/context/simple_context.go b/pkg/gui/context/simple_context.go index 36cd90d96..2de4199e2 100644 --- a/pkg/gui/context/simple_context.go +++ b/pkg/gui/context/simple_context.go @@ -33,10 +33,6 @@ func NewDisplayContext(key types.ContextKey, view *gocui.View, windowName string } func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) { - if self.hasSelectableContent { - self.GetViewTrait().SetHighlight(true) - } - for _, fn := range self.onFocusFns { fn(opts) } @@ -47,7 +43,6 @@ func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) { } func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) { - self.GetViewTrait().SetHighlight(false) self.view.SetOriginX(0) for _, fn := range self.onFocusLostFns { fn(opts) diff --git a/pkg/gui/context/view_trait.go b/pkg/gui/context/view_trait.go index 8e12e083f..9fc078e61 100644 --- a/pkg/gui/context/view_trait.go +++ b/pkg/gui/context/view_trait.go @@ -43,11 +43,6 @@ func (self *ViewTrait) SetContent(content string) { self.view.SetContent(content) } -func (self *ViewTrait) SetHighlight(highlight bool) { - self.view.Highlight = highlight - self.view.HighlightInactive = false -} - func (self *ViewTrait) SetFooter(value string) { self.view.Footer = value } diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index ddf7b345c..93b92c70e 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -229,7 +229,6 @@ type IViewTrait interface { ScrollDown(value int) PageDelta() int SelectedLineIdx() int - SetHighlight(bool) } type OnFocusOpts struct { diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 2151a5692..5f3e4c2ab 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -140,6 +140,10 @@ func (gui *Gui) postRefreshUpdate(c types.Context, opts types.OnFocusOpts) { c.HandleRender() + // The render may have given the context its first item, or taken its last one + // away, which decides whether its view draws a selection at all. + gui.State.ContextMgr.updateSelectionHighlights() + if gui.currentViewName() == c.GetInputViewName() { c.HandleFocus(opts) } else { diff --git a/pkg/integration/tests/ui/unfocused_list_hides_selection_when_emptied.go b/pkg/integration/tests/ui/unfocused_list_hides_selection_when_emptied.go index f6522770d..eef409cb2 100644 --- a/pkg/integration/tests/ui/unfocused_list_hides_selection_when_emptied.go +++ b/pkg/integration/tests/ui/unfocused_list_hides_selection_when_emptied.go @@ -31,9 +31,6 @@ var UnfocusedListHidesSelectionWhenEmptied = NewIntegrationTest(NewIntegrationTe t.RefreshInBackground() }). IsEmpty(). - /* EXPECTED: SelectionIsHidden() - ACTUAL: */ - SelectionIsInactive() }, }) diff --git a/pkg/integration/tests/ui/unfocused_list_shows_selection_when_filled.go b/pkg/integration/tests/ui/unfocused_list_shows_selection_when_filled.go index 9ff5388a5..78a8f210d 100644 --- a/pkg/integration/tests/ui/unfocused_list_shows_selection_when_filled.go +++ b/pkg/integration/tests/ui/unfocused_list_shows_selection_when_filled.go @@ -29,9 +29,6 @@ var UnfocusedListShowsSelectionWhenFilled = NewIntegrationTest(NewIntegrationTes t.RefreshInBackground() }). Lines(Contains("file2")). - /* EXPECTED: SelectionIsInactive() - ACTUAL: */ - SelectionIsHidden() }, })