1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-06 03:53:59 +02:00

mouse support

This commit is contained in:
Jesse Duffield Duffield 2019-02-25 22:11:35 +11:00 committed by Jesse Duffield
parent afbc028ad6
commit 8c0ea8f45f
11 changed files with 167 additions and 21 deletions

View File

@ -84,6 +84,10 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
} }
func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Branches panelState := gui.State.Panels.Branches
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
@ -94,6 +98,10 @@ func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Branches panelState := gui.State.Panels.Branches
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)

View File

@ -24,6 +24,13 @@ func (gui *Gui) getSelectedCommit(g *gocui.Gui) *commands.Commit {
} }
func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
commit := gui.getSelectedCommit(g) commit := gui.getSelectedCommit(g)
if commit == nil { if commit == nil {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch")) return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
@ -73,6 +80,10 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
} }
func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Commits panelState := gui.State.Panels.Commits
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false)
@ -83,6 +94,10 @@ func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleCommitsPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleCommitsPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Commits panelState := gui.State.Panels.Commits
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true)

View File

@ -15,6 +15,7 @@ func (gui *Gui) contextTitleMap() map[string]map[string]string {
"main": { "main": {
"staging": gui.Tr.SLocalize("StagingMainTitle"), "staging": gui.Tr.SLocalize("StagingMainTitle"),
"merging": gui.Tr.SLocalize("MergingMainTitle"), "merging": gui.Tr.SLocalize("MergingMainTitle"),
"normal": "",
}, },
} }
} }
@ -56,7 +57,7 @@ func (gui *Gui) setInitialContexts() error {
contextMap := gui.GetContextMap() contextMap := gui.GetContextMap()
initialContexts := map[string]string{ initialContexts := map[string]string{
"main": "merging", "main": "normal",
} }
for viewName, context := range initialContexts { for viewName, context := range initialContexts {

View File

@ -26,6 +26,30 @@ func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
return gui.State.Files[selectedLine], nil return gui.State.Files[selectedLine], nil
} }
func (gui *Gui) handleFilesFocus(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
cx, cy := v.Cursor()
_, oy := v.Origin()
prevSelectedLine := gui.State.Panels.Files.SelectedLine
newSelectedLine := cy - oy
if newSelectedLine > len(gui.State.Files)-1 || len(utils.Decolorise(gui.State.Files[newSelectedLine].DisplayString)) < cx {
return gui.handleFileSelect(gui.g, v, false)
}
gui.State.Panels.Files.SelectedLine = newSelectedLine
if prevSelectedLine == newSelectedLine && gui.currentViewName() == v.Name() {
return gui.handleFilePress(gui.g, v)
} else {
return gui.handleFileSelect(gui.g, v, true)
}
}
func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bool) error { func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View, alreadySelected bool) error {
if _, err := gui.g.SetCurrentView(v.Name()); err != nil { if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err return err
@ -87,6 +111,10 @@ func (gui *Gui) refreshFiles() error {
} }
func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Files panelState := gui.State.Panels.Files
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false)
@ -94,6 +122,10 @@ func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Files panelState := gui.State.Panels.Files
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true)

View File

@ -23,6 +23,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/updates" "github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -274,13 +275,14 @@ func (gui *Gui) onFocus(v *gocui.View) error {
func (gui *Gui) layout(g *gocui.Gui) error { func (gui *Gui) layout(g *gocui.Gui) error {
g.Highlight = true g.Highlight = true
width, height := g.Size() width, height := g.Size()
version := gui.Config.GetVersion() donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.SLocalize("Donate"))
version := donate + " " + gui.Config.GetVersion()
leftSideWidth := width / 3 leftSideWidth := width / 3
statusFilesBoundary := 2 statusFilesBoundary := 2
filesBranchesBoundary := 2 * height / 5 // height - 20 filesBranchesBoundary := 2 * height / 5 // height - 20
commitsBranchesBoundary := 3 * height / 5 // height - 10 commitsBranchesBoundary := 3 * height / 5 // height - 10
commitsStashBoundary := height - 5 // height - 5 commitsStashBoundary := height - 5 // height - 5
optionsVersionBoundary := width - max(len(version), 1) optionsVersionBoundary := width - max(len(utils.Decolorise(version)), 1)
minimumHeight := 16 minimumHeight := 16
minimumWidth := 10 minimumWidth := 10
@ -355,20 +357,22 @@ func (gui *Gui) layout(g *gocui.Gui) error {
branchesView.FgColor = gocui.ColorWhite branchesView.FgColor = gocui.ColorWhite
} }
if v, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM); err != nil { commitsView, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM)
if err != nil {
if err.Error() != "unknown view" { if err.Error() != "unknown view" {
return err return err
} }
v.Title = gui.Tr.SLocalize("CommitsTitle") commitsView.Title = gui.Tr.SLocalize("CommitsTitle")
v.FgColor = gocui.ColorWhite commitsView.FgColor = gocui.ColorWhite
} }
if v, err := g.SetView("stash", 0, commitsStashBoundary+panelSpacing, leftSideWidth, optionsTop, gocui.TOP|gocui.RIGHT); err != nil { stashView, err := g.SetView("stash", 0, commitsStashBoundary+panelSpacing, leftSideWidth, optionsTop, gocui.TOP|gocui.RIGHT)
if err != nil {
if err.Error() != "unknown view" { if err.Error() != "unknown view" {
return err return err
} }
v.Title = gui.Tr.SLocalize("StashTitle") stashView.Title = gui.Tr.SLocalize("StashTitle")
v.FgColor = gocui.ColorWhite stashView.FgColor = gocui.ColorWhite
} }
if v, err := g.SetView("options", appStatusOptionsBoundary-1, optionsTop, optionsVersionBoundary-1, optionsTop+2, 0); err != nil { if v, err := g.SetView("options", appStatusOptionsBoundary-1, optionsTop, optionsVersionBoundary-1, optionsTop+2, 0); err != nil {
@ -465,6 +469,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
listViews := map[*gocui.View]int{ listViews := map[*gocui.View]int{
filesView: gui.State.Panels.Files.SelectedLine, filesView: gui.State.Panels.Files.SelectedLine,
branchesView: gui.State.Panels.Branches.SelectedLine, branchesView: gui.State.Panels.Branches.SelectedLine,
commitsView: gui.State.Panels.Commits.SelectedLine,
stashView: gui.State.Panels.Stash.SelectedLine,
}
// menu view might not exist so we check to be safe
if menuView, err := gui.g.View("menu"); err == nil {
listViews[menuView] = gui.State.Panels.Menu.SelectedLine
} }
for view, selectedLine := range listViews { for view, selectedLine := range listViews {
// check if the selected line is now out of view and if so refocus it // check if the selected line is now out of view and if so refocus it
@ -543,6 +554,8 @@ func (gui *Gui) Run() error {
} }
defer g.Close() defer g.Close()
g.Mouse = true
gui.g = g // TODO: always use gui.g rather than passing g around everywhere gui.g = g // TODO: always use gui.g rather than passing g around everywhere
if err := gui.SetColorScheme(); err != nil { if err := gui.SetColorScheme(); err != nil {
@ -622,3 +635,11 @@ func (gui *Gui) quit(g *gocui.Gui, v *gocui.View) error {
} }
return gocui.ErrQuit return gocui.ErrQuit
} }
func (gui *Gui) handleDonate(g *gocui.Gui, v *gocui.View) error {
cx, _ := v.Cursor()
if cx > len(gui.Tr.SLocalize("Donate")) {
return nil
}
return gui.OSCommand.OpenLink("https://donorbox.org/lazygit")
}

View File

@ -400,7 +400,6 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleStashApply, Handler: gui.handleStashApply,
Description: gui.Tr.SLocalize("apply"), Description: gui.Tr.SLocalize("apply"),
}, { }, {
ViewName: "stash", ViewName: "stash",
Key: 'g', Key: 'g',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
@ -442,6 +441,11 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Key: 'q', Key: 'q',
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleMenuClose, Handler: gui.handleMenuClose,
}, {
ViewName: "version",
Key: gocui.MouseLeft,
Modifier: gocui.ModNone,
Handler: gui.handleDonate,
}, },
} }
@ -458,20 +462,25 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
listPanelMap := map[string]struct { listPanelMap := map[string]struct {
prevLine func(*gocui.Gui, *gocui.View) error prevLine func(*gocui.Gui, *gocui.View) error
nextLine func(*gocui.Gui, *gocui.View) error nextLine func(*gocui.Gui, *gocui.View) error
focus func(*gocui.Gui, *gocui.View) error
}{ }{
"menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine}, "menu": {prevLine: gui.handleMenuPrevLine, nextLine: gui.handleMenuNextLine, focus: gui.handleMenuSelect},
"files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine}, "files": {prevLine: gui.handleFilesPrevLine, nextLine: gui.handleFilesNextLine, focus: gui.handleFilesFocus},
"branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine}, "branches": {prevLine: gui.handleBranchesPrevLine, nextLine: gui.handleBranchesNextLine, focus: gui.handleBranchSelect},
"commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine}, "commits": {prevLine: gui.handleCommitsPrevLine, nextLine: gui.handleCommitsNextLine, focus: gui.handleCommitSelect},
"stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine}, "stash": {prevLine: gui.handleStashPrevLine, nextLine: gui.handleStashNextLine, focus: gui.handleStashEntrySelect},
"status": {focus: gui.handleStatusSelect},
} }
for viewName, functions := range listPanelMap { for viewName, functions := range listPanelMap {
bindings = append(bindings, []*Binding{ bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.prevLine}, {ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: functions.prevLine},
{ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.prevLine}, {ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: functions.prevLine},
{ViewName: viewName, Key: gocui.MouseWheelUp, Modifier: gocui.ModNone, Handler: functions.prevLine},
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.nextLine}, {ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: functions.nextLine},
{ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.nextLine}, {ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: functions.nextLine},
{ViewName: viewName, Key: gocui.MouseWheelDown, Modifier: gocui.ModNone, Handler: functions.nextLine},
{ViewName: viewName, Key: gocui.MouseLeft, Modifier: gocui.ModNone, Handler: functions.focus},
}...) }...)
} }
@ -481,7 +490,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
// GetCurrentKeybindings gets the list of keybindings given the current context // GetCurrentKeybindings gets the list of keybindings given the current context
func (gui *Gui) GetCurrentKeybindings() []*Binding { func (gui *Gui) GetCurrentKeybindings() []*Binding {
bindings := gui.GetInitialKeybindings() bindings := gui.GetInitialKeybindings()
viewName := gui.currentViewName(gui.g) viewName := gui.currentViewName()
currentContext := gui.State.Contexts[viewName] currentContext := gui.State.Contexts[viewName]
contextBindings := gui.GetContextMap()[viewName][currentContext] contextBindings := gui.GetContextMap()[viewName][currentContext]
@ -559,6 +568,16 @@ func (gui *Gui) GetContextMap() map[string]map[string][]*Binding {
Key: gocui.MouseWheelDown, Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleStagingNextLine, Handler: gui.handleStagingNextLine,
}, {
ViewName: "main",
Key: gocui.KeyArrowLeft,
Modifier: gocui.ModNone,
Handler: gui.handleStagingPrevLine,
}, {
ViewName: "main",
Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone,
Handler: gui.handleStagingNextLine,
}, { }, {
ViewName: "main", ViewName: "main",
Key: gocui.KeyArrowLeft, Key: gocui.KeyArrowLeft,
@ -640,6 +659,16 @@ func (gui *Gui) GetContextMap() map[string]map[string][]*Binding {
Modifier: gocui.ModNone, Modifier: gocui.ModNone,
Handler: gui.handleSelectBottom, Handler: gui.handleSelectBottom,
Description: gui.Tr.SLocalize("SelectBottom"), Description: gui.Tr.SLocalize("SelectBottom"),
}, {
ViewName: "main",
Key: gocui.MouseWheelUp,
Modifier: gocui.ModNone,
Handler: gui.handleSelectTop,
}, {
ViewName: "main",
Key: gocui.MouseWheelDown,
Modifier: gocui.ModNone,
Handler: gui.handleSelectBottom,
}, { }, {
ViewName: "main", ViewName: "main",
Key: 'h', Key: 'h',

View File

@ -205,7 +205,7 @@ func (gui *Gui) refreshMergePanel() error {
panelState.ConflictIndex = len(panelState.Conflicts) - 1 panelState.ConflictIndex = len(panelState.Conflicts) - 1
} }
hasFocus := gui.currentViewName(gui.g) == "main" hasFocus := gui.currentViewName() == "main"
content, err := gui.coloredConflictFile(cat, panelState.Conflicts, panelState.ConflictIndex, panelState.ConflictTop, hasFocus) content, err := gui.coloredConflictFile(cat, panelState.Conflicts, panelState.ConflictIndex, panelState.ConflictTop, hasFocus)
if err != nil { if err != nil {
return err return err

View File

@ -20,6 +20,13 @@ func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
} }
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
stashEntry := gui.getSelectedStashEntry(v) stashEntry := gui.getSelectedStashEntry(v)
if stashEntry == nil { if stashEntry == nil {
return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries")) return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries"))
@ -60,6 +67,10 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error {
} }
func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Stash panelState := gui.State.Panels.Stash
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false)
@ -70,6 +81,10 @@ func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleStashPrevLine(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStashPrevLine(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
panelState := gui.State.Panels.Stash panelState := gui.State.Panels.Stash
gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true) gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true)

View File

@ -48,6 +48,13 @@ func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
} }
func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
if gui.popupPanelFocused() {
return nil
}
if _, err := gui.g.SetCurrentView(v.Name()); err != nil {
return err
}
magenta := color.New(color.FgMagenta) magenta := color.New(color.FgMagenta)
dashboardString := strings.Join( dashboardString := strings.Join(

View File

@ -287,8 +287,8 @@ func (gui *Gui) trimmedContent(v *gocui.View) string {
return strings.TrimSpace(v.Buffer()) return strings.TrimSpace(v.Buffer())
} }
func (gui *Gui) currentViewName(g *gocui.Gui) string { func (gui *Gui) currentViewName() string {
currentView := g.CurrentView() currentView := gui.g.CurrentView()
return currentView.Name() return currentView.Name()
} }
@ -380,3 +380,20 @@ func (gui *Gui) renderPanelOptions() error {
} }
return gui.renderGlobalOptions() return gui.renderGlobalOptions()
} }
func (gui *Gui) handleFocusView(g *gocui.Gui, v *gocui.View) error {
_, err := gui.g.SetCurrentView(v.Name())
return err
}
func (gui *Gui) popupPanelFocused() bool {
viewNames := []string{"commitMessage",
"credentials",
"menu"}
for _, viewName := range viewNames {
if gui.currentViewName() == viewName {
return true
}
}
return false
}

View File

@ -160,7 +160,8 @@ func renderDisplayableList(items []Displayable, isFocused bool) (string, error)
return strings.Join(paddedDisplayStrings, "\n"), nil return strings.Join(paddedDisplayStrings, "\n"), nil
} }
func decolorise(str string) string { // Decolorise strips a string of color
func Decolorise(str string) string {
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`) re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
return re.ReplaceAllString(str, "") return re.ReplaceAllString(str, "")
} }
@ -172,7 +173,7 @@ func getPadWidths(stringArrays [][]string) []int {
padWidths := make([]int, len(stringArrays[0])-1) padWidths := make([]int, len(stringArrays[0])-1)
for i := range padWidths { for i := range padWidths {
for _, strings := range stringArrays { for _, strings := range stringArrays {
uncoloredString := decolorise(strings[i]) uncoloredString := Decolorise(strings[i])
if len(uncoloredString) > padWidths[i] { if len(uncoloredString) > padWidths[i] {
padWidths[i] = len(uncoloredString) padWidths[i] = len(uncoloredString)
} }