From ad93b4c863dfaa6a1cb6bb740d0dba87fef14404 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 16 Feb 2019 15:17:44 +1100 Subject: [PATCH] consider whether the view has focus when rendering the contents of a view --- pkg/commands/branch.go | 4 +-- pkg/commands/commit.go | 2 +- pkg/commands/file.go | 2 +- pkg/commands/stash_entry.go | 2 +- pkg/gui/commits_panel.go | 3 +- pkg/gui/files_panel.go | 3 +- pkg/gui/gui.go | 3 ++ pkg/gui/keybindings.go | 2 +- pkg/gui/menu_panel.go | 3 +- pkg/gui/rebase_options_panel.go | 2 +- pkg/gui/recent_repos_panel.go | 2 +- pkg/gui/stash_panel.go | 3 +- pkg/gui/view_helpers.go | 3 +- pkg/utils/utils.go | 14 ++++----- pkg/utils/utils_test.go | 50 +++++++++++++++++++++++++++++---- 15 files changed, 72 insertions(+), 26 deletions(-) diff --git a/pkg/commands/branch.go b/pkg/commands/branch.go index 419a106d2..6f7a1c31e 100644 --- a/pkg/commands/branch.go +++ b/pkg/commands/branch.go @@ -19,9 +19,9 @@ type Branch struct { } // GetDisplayStrings returns the dispaly string of branch -func (b *Branch) GetDisplayStrings() []string { +func (b *Branch) GetDisplayStrings(isFocused bool) []string { displayName := utils.ColoredString(b.Name, b.GetColor()) - if b.Selected && b.Pushables != "" && b.Pullables != "" { + if isFocused && b.Selected && b.Pushables != "" && b.Pullables != "" { displayName = fmt.Sprintf("%s ↑%s↓%s", displayName, b.Pushables, b.Pullables) } diff --git a/pkg/commands/commit.go b/pkg/commands/commit.go index 6ee972f9f..6ae6acd02 100644 --- a/pkg/commands/commit.go +++ b/pkg/commands/commit.go @@ -13,7 +13,7 @@ type Commit struct { } // GetDisplayStrings is a function. -func (c *Commit) GetDisplayStrings() []string { +func (c *Commit) GetDisplayStrings(isFocused bool) []string { red := color.New(color.FgRed) yellow := color.New(color.FgYellow) green := color.New(color.FgGreen) diff --git a/pkg/commands/file.go b/pkg/commands/file.go index 8fcd9aff9..3a5073af4 100644 --- a/pkg/commands/file.go +++ b/pkg/commands/file.go @@ -16,7 +16,7 @@ type File struct { } // GetDisplayStrings returns the display string of a file -func (f *File) GetDisplayStrings() []string { +func (f *File) GetDisplayStrings(isFocused bool) []string { // potentially inefficient to be instantiating these color // objects with each render red := color.New(color.FgRed) diff --git a/pkg/commands/stash_entry.go b/pkg/commands/stash_entry.go index 3886fd4c9..a9cfd95bf 100644 --- a/pkg/commands/stash_entry.go +++ b/pkg/commands/stash_entry.go @@ -8,6 +8,6 @@ type StashEntry struct { } // GetDisplayStrings returns the dispaly string of branch -func (s *StashEntry) GetDisplayStrings() []string { +func (s *StashEntry) GetDisplayStrings(isFocused bool) []string { return []string{s.DisplayString} } diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index 0c557b15d..e791cf87b 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -47,7 +47,8 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error { gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits)) - list, err := utils.RenderList(gui.State.Commits) + isFocused := gui.g.CurrentView().Name() == "commits" + list, err := utils.RenderList(gui.State.Commits, isFocused) if err != nil { return err } diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 57217249a..491b931e5 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -67,7 +67,8 @@ func (gui *Gui) refreshFiles() error { gui.g.Update(func(g *gocui.Gui) error { filesView.Clear() - list, err := utils.RenderList(gui.State.Files) + isFocused := gui.g.CurrentView().Name() == "files" + list, err := utils.RenderList(gui.State.Files, isFocused) if err != nil { return err } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 04653200d..637db680a 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -233,6 +233,9 @@ func (gui *Gui) onFocusLost(v *gocui.View) error { if v == nil { return nil } + if v.Name() == "branches" { + gui.renderListPanel(gui.getBranchesView(), gui.State.Branches) + } gui.Log.Info(v.Name() + " focus lost") return nil } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 5fe53ec8a..2b5672595 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -16,7 +16,7 @@ type Binding struct { } // GetDisplayStrings returns the display string of a file -func (b *Binding) GetDisplayStrings() []string { +func (b *Binding) GetDisplayStrings(isFocused bool) []string { return []string{b.GetKey(), b.Description} } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 847069446..6995a9fec 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -50,7 +50,8 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) createMenu(title string, items interface{}, handlePress func(int) error) error { - list, err := utils.RenderList(items) + isFocused := gui.g.CurrentView().Name() == "menu" + list, err := utils.RenderList(items, isFocused) if err != nil { return err } diff --git a/pkg/gui/rebase_options_panel.go b/pkg/gui/rebase_options_panel.go index f60542c90..7ecfc629d 100644 --- a/pkg/gui/rebase_options_panel.go +++ b/pkg/gui/rebase_options_panel.go @@ -12,7 +12,7 @@ type option struct { } // GetDisplayStrings is a function. -func (r *option) GetDisplayStrings() []string { +func (r *option) GetDisplayStrings(isFocused bool) []string { return []string{r.value} } diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go index e14a917eb..6108b737c 100644 --- a/pkg/gui/recent_repos_panel.go +++ b/pkg/gui/recent_repos_panel.go @@ -15,7 +15,7 @@ type recentRepo struct { } // GetDisplayStrings returns the path from a recent repo. -func (r *recentRepo) GetDisplayStrings() []string { +func (r *recentRepo) GetDisplayStrings(isFocused bool) []string { yellow := color.New(color.FgMagenta) base := filepath.Base(r.path) path := yellow.Sprint(r.path) diff --git a/pkg/gui/stash_panel.go b/pkg/gui/stash_panel.go index 4ee19793c..ed2f71401 100644 --- a/pkg/gui/stash_panel.go +++ b/pkg/gui/stash_panel.go @@ -41,7 +41,8 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { gui.refreshSelectedLine(&gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries)) - list, err := utils.RenderList(gui.State.StashEntries) + isFocused := gui.g.CurrentView().Name() == "stash" + list, err := utils.RenderList(gui.State.StashEntries, isFocused) if err != nil { return err } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index b7b23f16a..45c470978 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -364,7 +364,8 @@ func (gui *Gui) refreshSelectedLine(line *int, total int) { func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error { gui.g.Update(func(g *gocui.Gui) error { - list, err := utils.RenderList(items) + isFocused := gui.g.CurrentView().Name() == v.Name() + list, err := utils.RenderList(items, isFocused) if err != nil { return gui.createErrorPanel(gui.g, err.Error()) } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 60985dd0b..417823b84 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -113,13 +113,13 @@ func Min(x, y int) int { } type Displayable interface { - GetDisplayStrings() []string + GetDisplayStrings(bool) []string } // RenderList takes a slice of items, confirms they implement the Displayable // interface, then generates a list of their displaystrings to write to a panel's // buffer -func RenderList(slice interface{}) (string, error) { +func RenderList(slice interface{}, isFocused bool) (string, error) { s := reflect.ValueOf(slice) if s.Kind() != reflect.Slice { return "", errors.New("RenderList given a non-slice type") @@ -135,19 +135,19 @@ func RenderList(slice interface{}) (string, error) { displayables[i] = value } - return renderDisplayableList(displayables) + return renderDisplayableList(displayables, isFocused) } // renderDisplayableList takes a list of displayable items, obtains their display // strings via GetDisplayStrings() and then returns a single string containing // each item's string representation on its own line, with appropriate horizontal // padding between the item's own strings -func renderDisplayableList(items []Displayable) (string, error) { +func renderDisplayableList(items []Displayable, isFocused bool) (string, error) { if len(items) == 0 { return "", nil } - stringArrays := getDisplayStringArrays(items) + stringArrays := getDisplayStringArrays(items, isFocused) if !displayArraysAligned(stringArrays) { return "", errors.New("Each item must return the same number of strings to display") @@ -199,10 +199,10 @@ func displayArraysAligned(stringArrays [][]string) bool { return true } -func getDisplayStringArrays(displayables []Displayable) [][]string { +func getDisplayStringArrays(displayables []Displayable, isFocused bool) [][]string { stringArrays := make([][]string, len(displayables)) for i, item := range displayables { - stringArrays[i] = item.GetDisplayStrings() + stringArrays[i] = item.GetDisplayStrings(isFocused) } return stringArrays } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index ee502c1f8..1e232272f 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -203,15 +203,19 @@ type myDisplayable struct { type myStruct struct{} // GetDisplayStrings is a function. -func (d *myDisplayable) GetDisplayStrings() []string { +func (d *myDisplayable) GetDisplayStrings(isFocused bool) []string { + if isFocused { + return append(d.strings, "blah") + } return d.strings } // TestGetDisplayStringArrays is a function. func TestGetDisplayStringArrays(t *testing.T) { type scenario struct { - input []Displayable - expected [][]string + input []Displayable + isFocused bool + expected [][]string } scenarios := []scenario{ @@ -220,12 +224,21 @@ func TestGetDisplayStringArrays(t *testing.T) { Displayable(&myDisplayable{[]string{"a", "b"}}), Displayable(&myDisplayable{[]string{"c", "d"}}), }, + false, [][]string{{"a", "b"}, {"c", "d"}}, }, + { + []Displayable{ + Displayable(&myDisplayable{[]string{"a", "b"}}), + Displayable(&myDisplayable{[]string{"c", "d"}}), + }, + true, + [][]string{{"a", "b", "blah"}, {"c", "d", "blah"}}, + }, } for _, s := range scenarios { - assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input)) + assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input, s.isFocused)) } } @@ -233,6 +246,7 @@ func TestGetDisplayStringArrays(t *testing.T) { func TestRenderDisplayableList(t *testing.T) { type scenario struct { input []Displayable + isFocused bool expectedString string expectedErrorMessage string } @@ -243,6 +257,7 @@ func TestRenderDisplayableList(t *testing.T) { Displayable(&myDisplayable{[]string{}}), Displayable(&myDisplayable{[]string{}}), }, + false, "\n", "", }, @@ -251,6 +266,7 @@ func TestRenderDisplayableList(t *testing.T) { Displayable(&myDisplayable{[]string{"aa", "b"}}), Displayable(&myDisplayable{[]string{"c", "d"}}), }, + false, "aa b\nc d", "", }, @@ -259,13 +275,23 @@ func TestRenderDisplayableList(t *testing.T) { Displayable(&myDisplayable{[]string{"a"}}), Displayable(&myDisplayable{[]string{"b", "c"}}), }, + false, "", "Each item must return the same number of strings to display", }, + { + []Displayable{ + Displayable(&myDisplayable{[]string{"a"}}), + Displayable(&myDisplayable{[]string{"b"}}), + }, + true, + "a blah\nb blah", + "", + }, } for _, s := range scenarios { - str, err := renderDisplayableList(s.input) + str, err := renderDisplayableList(s.input, s.isFocused) assert.EqualValues(t, s.expectedString, str) if s.expectedErrorMessage != "" { assert.EqualError(t, err, s.expectedErrorMessage) @@ -279,6 +305,7 @@ func TestRenderDisplayableList(t *testing.T) { func TestRenderList(t *testing.T) { type scenario struct { input interface{} + isFocused bool expectedString string expectedErrorMessage string } @@ -289,6 +316,7 @@ func TestRenderList(t *testing.T) { {[]string{"aa", "b"}}, {[]string{"c", "d"}}, }, + false, "aa b\nc d", "", }, @@ -297,18 +325,28 @@ func TestRenderList(t *testing.T) { {}, {}, }, + false, "", "item does not implement the Displayable interface", }, { &myStruct{}, + false, "", "RenderList given a non-slice type", }, + { + []*myDisplayable{ + {[]string{"a"}}, + }, + true, + "a blah", + "", + }, } for _, s := range scenarios { - str, err := RenderList(s.input) + str, err := RenderList(s.input, s.isFocused) assert.EqualValues(t, s.expectedString, str) if s.expectedErrorMessage != "" { assert.EqualError(t, err, s.expectedErrorMessage)