diff --git a/pkg/gui/context/list_context_trait.go b/pkg/gui/context/list_context_trait.go index cdc20a85b96..ca3b3254f32 100644 --- a/pkg/gui/context/list_context_trait.go +++ b/pkg/gui/context/list_context_trait.go @@ -97,3 +97,16 @@ func (self *ListContextTrait) OnSearchSelect(selectedLineIdx int) error { self.GetList().SetSelectedLineIdx(selectedLineIdx) return self.HandleFocus(types.OnFocusOpts{}) } + +func (self *ListContextTrait) IsItemVisible(item types.HasUrn) bool { + startIdx, length := self.GetViewTrait().ViewPortYBounds() + selectionStart := self.ViewIndexToModelIndex(startIdx) + selectionEnd := self.ViewIndexToModelIndex(startIdx + length) + for i := selectionStart; i < selectionEnd; i++ { + iterItem := self.GetList().GetItem(i) + if iterItem != nil && iterItem.URN() == item.URN() { + return true + } + } + return false +} diff --git a/pkg/gui/context/list_view_model.go b/pkg/gui/context/list_view_model.go index b70330d7d64..22416bff1f4 100644 --- a/pkg/gui/context/list_view_model.go +++ b/pkg/gui/context/list_view_model.go @@ -1,6 +1,9 @@ package context -import "github.com/jesseduffield/lazygit/pkg/gui/context/traits" +import ( + "github.com/jesseduffield/lazygit/pkg/gui/context/traits" + "github.com/jesseduffield/lazygit/pkg/gui/types" +) type ListViewModel[T any] struct { *traits.ListCursor @@ -36,3 +39,8 @@ func (self *ListViewModel[T]) GetItems() []T { func Zero[T any]() T { return *new(T) } + +func (self *ListViewModel[T]) GetItem(index int) types.HasUrn { + item := self.getModel()[index] + return any(item).(types.HasUrn) +} diff --git a/pkg/gui/controllers.go b/pkg/gui/controllers.go index 5a104352bc3..37c54a655a5 100644 --- a/pkg/gui/controllers.go +++ b/pkg/gui/controllers.go @@ -115,7 +115,7 @@ func (gui *Gui) resetHelpersAndControllers() { Confirmation: helpers.NewConfirmationHelper(helperCommon), Mode: modeHelper, AppStatus: appStatusHelper, - InlineStatus: helpers.NewInlineStatusHelper(helperCommon), + InlineStatus: helpers.NewInlineStatusHelper(helperCommon, windowHelper), WindowArrangement: helpers.NewWindowArrangementHelper( gui.c, windowHelper, diff --git a/pkg/gui/controllers/helpers/inline_status_helper.go b/pkg/gui/controllers/helpers/inline_status_helper.go index d4435e5b983..7368986bde4 100644 --- a/pkg/gui/controllers/helpers/inline_status_helper.go +++ b/pkg/gui/controllers/helpers/inline_status_helper.go @@ -4,6 +4,7 @@ import ( "time" "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/gui/presentation" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/sasha-s/go-deadlock" @@ -12,13 +13,15 @@ import ( type InlineStatusHelper struct { c *HelperCommon + windowHelper *WindowHelper contextsWithInlineStatus map[types.ContextKey]*inlineStatusInfo mutex *deadlock.Mutex } -func NewInlineStatusHelper(c *HelperCommon) *InlineStatusHelper { +func NewInlineStatusHelper(c *HelperCommon, windowHelper *WindowHelper) *InlineStatusHelper { return &InlineStatusHelper{ c: c, + windowHelper: windowHelper, contextsWithInlineStatus: make(map[types.ContextKey]*inlineStatusInfo), mutex: &deadlock.Mutex{}, } @@ -61,18 +64,33 @@ func (self inlineStatusHelperTask) Continue() { } func (self *InlineStatusHelper) WithInlineStatus(opts InlineStatusOpts, f func(gocui.Task) error) { - self.c.OnWorker(func(task gocui.Task) { - self.start(opts) - - err := f(inlineStatusHelperTask{task, self, opts}) - if err != nil { - self.c.OnUIThread(func() error { - return self.c.Error(err) - }) - } + context := self.c.ContextForKey(opts.ContextKey).(types.IListContext) + view := context.GetView() + visible := view.Visible && self.windowHelper.TopViewInWindow(context.GetWindowName()) == view + if visible && context.IsItemVisible(opts.Item) { + self.c.OnWorker(func(task gocui.Task) { + self.start(opts) + + err := f(inlineStatusHelperTask{task, self, opts}) + if err != nil { + self.c.OnUIThread(func() error { + return self.c.Error(err) + }) + } - self.stop(opts) - }) + self.stop(opts) + }) + } else { + message := presentation.ItemOperationToString(opts.Operation, self.c.Tr) + _ = self.c.WithWaitingStatus(message, func(t gocui.Task) error { + // We still need to set the item operation, because it might be used + // for other (non-presentation) purposes + self.c.State().SetItemOperation(opts.Item, opts.Operation) + defer self.c.State().ClearItemOperation(opts.Item) + + return f(t) + }) + } } func (self *InlineStatusHelper) start(opts InlineStatusOpts) { diff --git a/pkg/gui/controllers/helpers/window_helper.go b/pkg/gui/controllers/helpers/window_helper.go index efe84c3984b..4bdd7a88972 100644 --- a/pkg/gui/controllers/helpers/window_helper.go +++ b/pkg/gui/controllers/helpers/window_helper.go @@ -92,7 +92,7 @@ func (self *WindowHelper) MoveToTopOfWindow(context types.Context) { topView := self.TopViewInWindow(window) - if view.Name() != topView.Name() { + if topView != nil && view.Name() != topView.Name() { if err := self.c.GocuiGui().SetViewOnTopOf(view.Name(), topView.Name()); err != nil { self.c.Log.Error(err) } @@ -106,7 +106,7 @@ func (self *WindowHelper) TopViewInWindow(windowName string) *gocui.View { // The views list is ordered highest-last, so we're grabbing the last view of the window var topView *gocui.View for _, currentView := range self.c.GocuiGui().Views() { - if lo.Contains(viewNamesInWindow, currentView.Name()) { + if lo.Contains(viewNamesInWindow, currentView.Name()) && currentView.Visible { topView = currentView } } diff --git a/pkg/gui/filetree/commit_file_tree.go b/pkg/gui/filetree/commit_file_tree.go index 59389944364..2593828eea3 100644 --- a/pkg/gui/filetree/commit_file_tree.go +++ b/pkg/gui/filetree/commit_file_tree.go @@ -2,6 +2,7 @@ package filetree import ( "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/samber/lo" "github.com/sirupsen/logrus" ) @@ -69,6 +70,11 @@ func (self *CommitFileTree) Len() int { return self.tree.Size(self.collapsedPaths) - 1 // ignoring root } +func (self *CommitFileTree) GetItem(index int) types.HasUrn { + // Unimplemented because we don't yet need to show inlines statuses in commit file views + return nil +} + func (self *CommitFileTree) GetAllFiles() []*models.CommitFile { return self.getFiles() } diff --git a/pkg/gui/filetree/file_tree.go b/pkg/gui/filetree/file_tree.go index 45cfeed4066..9d3bb580dc9 100644 --- a/pkg/gui/filetree/file_tree.go +++ b/pkg/gui/filetree/file_tree.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/samber/lo" "github.com/sirupsen/logrus" ) @@ -24,6 +25,7 @@ type ITree[T any] interface { ToggleShowTree() GetIndexForPath(path string) (int, bool) Len() int + GetItem(index int) types.HasUrn SetTree() IsCollapsed(path string) bool ToggleCollapsed(path string) @@ -139,6 +141,11 @@ func (self *FileTree) Len() int { return self.tree.Size(self.collapsedPaths) - 1 // ignoring root } +func (self *FileTree) GetItem(index int) types.HasUrn { + // Unimplemented because we don't yet need to show inlines statuses in commit file views + return nil +} + func (self *FileTree) GetAllFiles() []*models.File { return self.getFiles() } diff --git a/pkg/gui/main_panels.go b/pkg/gui/main_panels.go index d3418b6285d..3dee86f1b0e 100644 --- a/pkg/gui/main_panels.go +++ b/pkg/gui/main_panels.go @@ -39,12 +39,8 @@ func (gui *Gui) moveMainContextToTop(context types.Context) { view := context.GetView() topView := gui.helpers.Window.TopViewInWindow(context.GetWindowName()) - if topView == nil { - gui.Log.Error("unexpected: topView is nil") - return - } - if topView != view { + if topView != nil && topView != view { // We need to copy the content to avoid a flicker effect: If we're flicking // through files in the files panel, we use a different view to render the // files vs the directories, and if you select dir A, then file B, then dir diff --git a/pkg/gui/presentation/branches.go b/pkg/gui/presentation/branches.go index a2e9f139f76..a2bb97ed2b4 100644 --- a/pkg/gui/presentation/branches.go +++ b/pkg/gui/presentation/branches.go @@ -163,7 +163,7 @@ func ColoredBranchStatus(branch *models.Branch, itemOperation types.ItemOperatio } func BranchStatus(branch *models.Branch, itemOperation types.ItemOperation, tr *i18n.TranslationSet, now time.Time) string { - itemOperationStr := itemOperationToString(itemOperation, tr) + itemOperationStr := ItemOperationToString(itemOperation, tr) if itemOperationStr != "" { return itemOperationStr + " " + utils.Loader(now) } diff --git a/pkg/gui/presentation/item_operations.go b/pkg/gui/presentation/item_operations.go index afa48da4f45..85b81a12351 100644 --- a/pkg/gui/presentation/item_operations.go +++ b/pkg/gui/presentation/item_operations.go @@ -5,7 +5,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/i18n" ) -func itemOperationToString(itemOperation types.ItemOperation, tr *i18n.TranslationSet) string { +func ItemOperationToString(itemOperation types.ItemOperation, tr *i18n.TranslationSet) string { switch itemOperation { case types.ItemOperationNone: return "" diff --git a/pkg/gui/presentation/tags.go b/pkg/gui/presentation/tags.go index c210bdebc1e..2d3a7375513 100644 --- a/pkg/gui/presentation/tags.go +++ b/pkg/gui/presentation/tags.go @@ -37,7 +37,7 @@ func getTagDisplayStrings(t *models.Tag, itemOperation types.ItemOperation, diff } descriptionColor := style.FgYellow descriptionStr := descriptionColor.Sprint(t.Description()) - itemOperationStr := itemOperationToString(itemOperation, tr) + itemOperationStr := ItemOperationToString(itemOperation, tr) if itemOperationStr != "" { descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now())) + " " + descriptionStr } diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index aca694228aa..82f03c70f7c 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -136,6 +136,7 @@ type IListContext interface { Context GetSelectedItemId() string + IsItemVisible(item HasUrn) bool GetList() IList ViewIndexToModelIndex(int) int @@ -215,6 +216,7 @@ type IController interface { type IList interface { IListCursor Len() int + GetItem(index int) HasUrn } type IListCursor interface {