From e883f74f3c285bb44217e3fdd455d8cfa1bfbe45 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 1 Feb 2025 18:40:17 +0100 Subject: [PATCH 1/3] Allow user to switch filter when showing only conflicts We don't need to maintain additional state to allow this; all we need to do is take over the filter only when the number of conflicting files goes from zero to non-zero, rather than every time it is non-zero. The only problem is that we don't allow users to go back to showing only conflicted files, but that's just because we don't have that as an entry in the menu. And I don't think it's a problem. --- pkg/gui/controllers/helpers/refresh_helper.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 37e451895ca..64543507851 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -588,15 +588,11 @@ func (self *RefreshHelper) refreshStateFiles() error { fileTreeViewModel.RWMutex.Lock() // only taking over the filter if it hasn't already been set by the user. - // Though this does make it impossible for the user to actually say they want to display all if - // conflicts are currently being shown. Hmm. Worth it I reckon. If we need to add some - // extra state here to see if the user's set the filter themselves we can do that, but - // I'd prefer to maintain as little state as possible. - if conflictFileCount > 0 { + if conflictFileCount > 0 && prevConflictFileCount == 0 { if fileTreeViewModel.GetFilter() == filetree.DisplayAll { fileTreeViewModel.SetStatusFilter(filetree.DisplayConflicted) } - } else if fileTreeViewModel.GetFilter() == filetree.DisplayConflicted { + } else if conflictFileCount == 0 && fileTreeViewModel.GetFilter() == filetree.DisplayConflicted { fileTreeViewModel.SetStatusFilter(filetree.DisplayAll) } From 2f4cedd02547fd95e03f8b218a221548e5dfa197 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 1 Feb 2025 18:47:29 +0100 Subject: [PATCH 2/3] Show current files filter as radio buttons I renamed the "Reset filter" item to "No filter" to make it look more like a state than an action, so that it fits the radio button concept better. When there are conflicts and we set the filter to show only conflicting files, then none of the radio buttons light up, which is slightly strange. I guess it's ok though. --- pkg/gui/controllers/files_controller.go | 18 ++++++++++++------ pkg/i18n/english.go | 4 ++-- pkg/integration/tests/conflicts/filter.go | 2 +- .../filter_and_search/filter_by_file_status.go | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index cdf3f6241ec..50f51c35aff 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -753,6 +753,7 @@ func (self *FilesController) isResolvingConflicts() bool { } func (self *FilesController) handleStatusFilterPressed() error { + currentFilter := self.context().GetFilter() return self.c.Menu(types.CreateMenuOptions{ Title: self.c.Tr.FilteringMenuTitle, Items: []*types.MenuItem{ @@ -761,35 +762,40 @@ func (self *FilesController) handleStatusFilterPressed() error { OnPress: func() error { return self.setStatusFiltering(filetree.DisplayStaged) }, - Key: 's', + Key: 's', + Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayStaged), }, { Label: self.c.Tr.FilterUnstagedFiles, OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUnstaged) }, - Key: 'u', + Key: 'u', + Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUnstaged), }, { Label: self.c.Tr.FilterTrackedFiles, OnPress: func() error { return self.setStatusFiltering(filetree.DisplayTracked) }, - Key: 't', + Key: 't', + Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayTracked), }, { Label: self.c.Tr.FilterUntrackedFiles, OnPress: func() error { return self.setStatusFiltering(filetree.DisplayUntracked) }, - Key: 'T', + Key: 'T', + Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayUntracked), }, { - Label: self.c.Tr.ResetFilter, + Label: self.c.Tr.NoFilter, OnPress: func() error { return self.setStatusFiltering(filetree.DisplayAll) }, - Key: 'r', + Key: 'r', + Widget: types.MakeMenuRadioButton(currentFilter == filetree.DisplayAll), }, }, }) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 135ab43e1c1..7bf771e1fb6 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -89,7 +89,7 @@ type TranslationSet struct { FilterUnstagedFiles string FilterTrackedFiles string FilterUntrackedFiles string - ResetFilter string + NoFilter string MergeConflictsTitle string Checkout string CheckoutTooltip string @@ -1115,7 +1115,7 @@ func EnglishTranslationSet() *TranslationSet { FilterUnstagedFiles: "Show only unstaged files", FilterTrackedFiles: "Show only tracked files", FilterUntrackedFiles: "Show only untracked files", - ResetFilter: "Reset filter", + NoFilter: "No filter", NoChangedFiles: "No changed files", SoftReset: "Soft reset", AlreadyCheckedOutBranch: "You have already checked out this branch", diff --git a/pkg/integration/tests/conflicts/filter.go b/pkg/integration/tests/conflicts/filter.go index 32f5a8cd280..c997d8daaff 100644 --- a/pkg/integration/tests/conflicts/filter.go +++ b/pkg/integration/tests/conflicts/filter.go @@ -25,7 +25,7 @@ var Filter = NewIntegrationTest(NewIntegrationTestArgs{ Tap(func() { t.ExpectPopup().Menu(). Title(Equals("Filtering")). - Select(Contains("Reset filter")). + Select(Contains("No filter")). Confirm() }). Lines( diff --git a/pkg/integration/tests/filter_and_search/filter_by_file_status.go b/pkg/integration/tests/filter_and_search/filter_by_file_status.go index 05b38ea962b..f2335d28c07 100644 --- a/pkg/integration/tests/filter_and_search/filter_by_file_status.go +++ b/pkg/integration/tests/filter_and_search/filter_by_file_status.go @@ -52,7 +52,7 @@ var FilterByFileStatus = NewIntegrationTest(NewIntegrationTestArgs{ Tap(func() { t.ExpectPopup().Menu(). Title(Equals("Filtering")). - Select(Contains("Reset filter")). + Select(Contains("No filter")). Confirm() }). Lines( From aad2622278d02d355acb6b81b5e928e4ce2ad559 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sat, 1 Feb 2025 18:21:11 +0100 Subject: [PATCH 3/3] Show filter state in top right corner of Files panel frame This includes the "only conflicting" status that the user can't switch to themselves. We display it anyway to give a hint that files are being filtered, and to let them know that they can turn the filter off if they want to. --- pkg/gui/controllers/files_controller.go | 21 +++++++++++++++++++ pkg/gui/controllers/helpers/refresh_helper.go | 2 ++ pkg/i18n/english.go | 10 +++++++++ 3 files changed, 33 insertions(+) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 50f51c35aff..34065bf3371 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -2,6 +2,7 @@ package controllers import ( "errors" + "fmt" "strings" "github.com/jesseduffield/gocui" @@ -801,10 +802,30 @@ func (self *FilesController) handleStatusFilterPressed() error { }) } +func (self *FilesController) filteringLabel(filter filetree.FileTreeDisplayFilter) string { + switch filter { + case filetree.DisplayAll: + return "" + case filetree.DisplayStaged: + return self.c.Tr.FilterLabelStagedFiles + case filetree.DisplayUnstaged: + return self.c.Tr.FilterLabelUnstagedFiles + case filetree.DisplayTracked: + return self.c.Tr.FilterLabelTrackedFiles + case filetree.DisplayUntracked: + return self.c.Tr.FilterLabelUntrackedFiles + case filetree.DisplayConflicted: + return self.c.Tr.FilterLabelConflictingFiles + } + + panic(fmt.Sprintf("Unexpected files display filter: %d", filter)) +} + func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error { previousFilter := self.context().GetFilter() self.context().FileTreeViewModel.SetStatusFilter(filter) + self.c.Contexts().Files.GetView().Subtitle = self.filteringLabel(filter) // Whenever we switch between untracked and other filters, we need to refresh the files view // because the untracked files filter applies when running `git status`. diff --git a/pkg/gui/controllers/helpers/refresh_helper.go b/pkg/gui/controllers/helpers/refresh_helper.go index 64543507851..b9a90af606a 100644 --- a/pkg/gui/controllers/helpers/refresh_helper.go +++ b/pkg/gui/controllers/helpers/refresh_helper.go @@ -591,9 +591,11 @@ func (self *RefreshHelper) refreshStateFiles() error { if conflictFileCount > 0 && prevConflictFileCount == 0 { if fileTreeViewModel.GetFilter() == filetree.DisplayAll { fileTreeViewModel.SetStatusFilter(filetree.DisplayConflicted) + self.c.Contexts().Files.GetView().Subtitle = self.c.Tr.FilterLabelConflictingFiles } } else if conflictFileCount == 0 && fileTreeViewModel.GetFilter() == filetree.DisplayConflicted { fileTreeViewModel.SetStatusFilter(filetree.DisplayAll) + self.c.Contexts().Files.GetView().Subtitle = "" } self.c.Model().Files = files diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 7bf771e1fb6..a5584667a09 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -90,6 +90,11 @@ type TranslationSet struct { FilterTrackedFiles string FilterUntrackedFiles string NoFilter string + FilterLabelStagedFiles string + FilterLabelUnstagedFiles string + FilterLabelTrackedFiles string + FilterLabelUntrackedFiles string + FilterLabelConflictingFiles string MergeConflictsTitle string Checkout string CheckoutTooltip string @@ -1116,6 +1121,11 @@ func EnglishTranslationSet() *TranslationSet { FilterTrackedFiles: "Show only tracked files", FilterUntrackedFiles: "Show only untracked files", NoFilter: "No filter", + FilterLabelStagedFiles: "(only staged)", + FilterLabelUnstagedFiles: "(only unstaged)", + FilterLabelTrackedFiles: "(only tracked)", + FilterLabelUntrackedFiles: "(only untracked)", + FilterLabelConflictingFiles: "(only conflicting)", NoChangedFiles: "No changed files", SoftReset: "Soft reset", AlreadyCheckedOutBranch: "You have already checked out this branch",