consider whether the view has focus when rendering the contents of a view

This commit is contained in:
Jesse Duffield 2019-02-16 15:17:44 +11:00
parent 198cbee498
commit ad93b4c863
15 changed files with 72 additions and 26 deletions

View file

@ -19,9 +19,9 @@ type Branch struct {
} }
// GetDisplayStrings returns the dispaly string of branch // 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()) 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) displayName = fmt.Sprintf("%s ↑%s↓%s", displayName, b.Pushables, b.Pullables)
} }

View file

@ -13,7 +13,7 @@ type Commit struct {
} }
// GetDisplayStrings is a function. // GetDisplayStrings is a function.
func (c *Commit) GetDisplayStrings() []string { func (c *Commit) GetDisplayStrings(isFocused bool) []string {
red := color.New(color.FgRed) red := color.New(color.FgRed)
yellow := color.New(color.FgYellow) yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen) green := color.New(color.FgGreen)

View file

@ -16,7 +16,7 @@ type File struct {
} }
// GetDisplayStrings returns the display string of a file // 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 // potentially inefficient to be instantiating these color
// objects with each render // objects with each render
red := color.New(color.FgRed) red := color.New(color.FgRed)

View file

@ -8,6 +8,6 @@ type StashEntry struct {
} }
// GetDisplayStrings returns the dispaly string of branch // GetDisplayStrings returns the dispaly string of branch
func (s *StashEntry) GetDisplayStrings() []string { func (s *StashEntry) GetDisplayStrings(isFocused bool) []string {
return []string{s.DisplayString} return []string{s.DisplayString}
} }

View file

@ -47,7 +47,8 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits)) 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 { if err != nil {
return err return err
} }

View file

@ -67,7 +67,8 @@ func (gui *Gui) refreshFiles() error {
gui.g.Update(func(g *gocui.Gui) error { gui.g.Update(func(g *gocui.Gui) error {
filesView.Clear() 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 { if err != nil {
return err return err
} }

View file

@ -233,6 +233,9 @@ func (gui *Gui) onFocusLost(v *gocui.View) error {
if v == nil { if v == nil {
return nil return nil
} }
if v.Name() == "branches" {
gui.renderListPanel(gui.getBranchesView(), gui.State.Branches)
}
gui.Log.Info(v.Name() + " focus lost") gui.Log.Info(v.Name() + " focus lost")
return nil return nil
} }

View file

@ -16,7 +16,7 @@ type Binding struct {
} }
// GetDisplayStrings returns the display string of a file // 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} return []string{b.GetKey(), b.Description}
} }

View file

@ -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 { 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 { if err != nil {
return err return err
} }

View file

@ -12,7 +12,7 @@ type option struct {
} }
// GetDisplayStrings is a function. // GetDisplayStrings is a function.
func (r *option) GetDisplayStrings() []string { func (r *option) GetDisplayStrings(isFocused bool) []string {
return []string{r.value} return []string{r.value}
} }

View file

@ -15,7 +15,7 @@ type recentRepo struct {
} }
// GetDisplayStrings returns the path from a recent repo. // 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) yellow := color.New(color.FgMagenta)
base := filepath.Base(r.path) base := filepath.Base(r.path)
path := yellow.Sprint(r.path) path := yellow.Sprint(r.path)

View file

@ -41,7 +41,8 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
gui.refreshSelectedLine(&gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries)) 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 { if err != nil {
return err return err
} }

View file

@ -364,7 +364,8 @@ func (gui *Gui) refreshSelectedLine(line *int, total int) {
func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error { func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
gui.g.Update(func(g *gocui.Gui) 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 { if err != nil {
return gui.createErrorPanel(gui.g, err.Error()) return gui.createErrorPanel(gui.g, err.Error())
} }

View file

@ -113,13 +113,13 @@ func Min(x, y int) int {
} }
type Displayable interface { type Displayable interface {
GetDisplayStrings() []string GetDisplayStrings(bool) []string
} }
// RenderList takes a slice of items, confirms they implement the Displayable // 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 // interface, then generates a list of their displaystrings to write to a panel's
// buffer // buffer
func RenderList(slice interface{}) (string, error) { func RenderList(slice interface{}, isFocused bool) (string, error) {
s := reflect.ValueOf(slice) s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice { if s.Kind() != reflect.Slice {
return "", errors.New("RenderList given a non-slice type") return "", errors.New("RenderList given a non-slice type")
@ -135,19 +135,19 @@ func RenderList(slice interface{}) (string, error) {
displayables[i] = value displayables[i] = value
} }
return renderDisplayableList(displayables) return renderDisplayableList(displayables, isFocused)
} }
// renderDisplayableList takes a list of displayable items, obtains their display // renderDisplayableList takes a list of displayable items, obtains their display
// strings via GetDisplayStrings() and then returns a single string containing // strings via GetDisplayStrings() and then returns a single string containing
// each item's string representation on its own line, with appropriate horizontal // each item's string representation on its own line, with appropriate horizontal
// padding between the item's own strings // 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 { if len(items) == 0 {
return "", nil return "", nil
} }
stringArrays := getDisplayStringArrays(items) stringArrays := getDisplayStringArrays(items, isFocused)
if !displayArraysAligned(stringArrays) { if !displayArraysAligned(stringArrays) {
return "", errors.New("Each item must return the same number of strings to display") 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 return true
} }
func getDisplayStringArrays(displayables []Displayable) [][]string { func getDisplayStringArrays(displayables []Displayable, isFocused bool) [][]string {
stringArrays := make([][]string, len(displayables)) stringArrays := make([][]string, len(displayables))
for i, item := range displayables { for i, item := range displayables {
stringArrays[i] = item.GetDisplayStrings() stringArrays[i] = item.GetDisplayStrings(isFocused)
} }
return stringArrays return stringArrays
} }

View file

@ -203,15 +203,19 @@ type myDisplayable struct {
type myStruct struct{} type myStruct struct{}
// GetDisplayStrings is a function. // 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 return d.strings
} }
// TestGetDisplayStringArrays is a function. // TestGetDisplayStringArrays is a function.
func TestGetDisplayStringArrays(t *testing.T) { func TestGetDisplayStringArrays(t *testing.T) {
type scenario struct { type scenario struct {
input []Displayable input []Displayable
expected [][]string isFocused bool
expected [][]string
} }
scenarios := []scenario{ scenarios := []scenario{
@ -220,12 +224,21 @@ func TestGetDisplayStringArrays(t *testing.T) {
Displayable(&myDisplayable{[]string{"a", "b"}}), Displayable(&myDisplayable{[]string{"a", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}), Displayable(&myDisplayable{[]string{"c", "d"}}),
}, },
false,
[][]string{{"a", "b"}, {"c", "d"}}, [][]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 { 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) { func TestRenderDisplayableList(t *testing.T) {
type scenario struct { type scenario struct {
input []Displayable input []Displayable
isFocused bool
expectedString string expectedString string
expectedErrorMessage string expectedErrorMessage string
} }
@ -243,6 +257,7 @@ func TestRenderDisplayableList(t *testing.T) {
Displayable(&myDisplayable{[]string{}}), Displayable(&myDisplayable{[]string{}}),
Displayable(&myDisplayable{[]string{}}), Displayable(&myDisplayable{[]string{}}),
}, },
false,
"\n", "\n",
"", "",
}, },
@ -251,6 +266,7 @@ func TestRenderDisplayableList(t *testing.T) {
Displayable(&myDisplayable{[]string{"aa", "b"}}), Displayable(&myDisplayable{[]string{"aa", "b"}}),
Displayable(&myDisplayable{[]string{"c", "d"}}), Displayable(&myDisplayable{[]string{"c", "d"}}),
}, },
false,
"aa b\nc d", "aa b\nc d",
"", "",
}, },
@ -259,13 +275,23 @@ func TestRenderDisplayableList(t *testing.T) {
Displayable(&myDisplayable{[]string{"a"}}), Displayable(&myDisplayable{[]string{"a"}}),
Displayable(&myDisplayable{[]string{"b", "c"}}), Displayable(&myDisplayable{[]string{"b", "c"}}),
}, },
false,
"", "",
"Each item must return the same number of strings to display", "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 { for _, s := range scenarios {
str, err := renderDisplayableList(s.input) str, err := renderDisplayableList(s.input, s.isFocused)
assert.EqualValues(t, s.expectedString, str) assert.EqualValues(t, s.expectedString, str)
if s.expectedErrorMessage != "" { if s.expectedErrorMessage != "" {
assert.EqualError(t, err, s.expectedErrorMessage) assert.EqualError(t, err, s.expectedErrorMessage)
@ -279,6 +305,7 @@ func TestRenderDisplayableList(t *testing.T) {
func TestRenderList(t *testing.T) { func TestRenderList(t *testing.T) {
type scenario struct { type scenario struct {
input interface{} input interface{}
isFocused bool
expectedString string expectedString string
expectedErrorMessage string expectedErrorMessage string
} }
@ -289,6 +316,7 @@ func TestRenderList(t *testing.T) {
{[]string{"aa", "b"}}, {[]string{"aa", "b"}},
{[]string{"c", "d"}}, {[]string{"c", "d"}},
}, },
false,
"aa b\nc d", "aa b\nc d",
"", "",
}, },
@ -297,18 +325,28 @@ func TestRenderList(t *testing.T) {
{}, {},
{}, {},
}, },
false,
"", "",
"item does not implement the Displayable interface", "item does not implement the Displayable interface",
}, },
{ {
&myStruct{}, &myStruct{},
false,
"", "",
"RenderList given a non-slice type", "RenderList given a non-slice type",
}, },
{
[]*myDisplayable{
{[]string{"a"}},
},
true,
"a blah",
"",
},
} }
for _, s := range scenarios { for _, s := range scenarios {
str, err := RenderList(s.input) str, err := RenderList(s.input, s.isFocused)
assert.EqualValues(t, s.expectedString, str) assert.EqualValues(t, s.expectedString, str)
if s.expectedErrorMessage != "" { if s.expectedErrorMessage != "" {
assert.EqualError(t, err, s.expectedErrorMessage) assert.EqualError(t, err, s.expectedErrorMessage)