1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-15 00:15:32 +02:00
This commit is contained in:
Jesse Duffield
2020-08-16 13:58:29 +10:00
parent 0ea0c48631
commit 7f89113245
30 changed files with 615 additions and 461 deletions

View File

@ -244,27 +244,22 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
}
func (gui *Gui) currentCyclableViewName() string {
currView := gui.g.CurrentView()
currentCyclebleView := gui.State.PreviousView
if currView != nil {
viewName := currView.Name()
usePreviousView := true
for _, view := range gui.getCyclableViews() {
if view == viewName {
currentCyclebleView = viewName
usePreviousView = false
break
}
}
if usePreviousView {
currentCyclebleView = gui.State.PreviousView
}
}
// there is always a cyclable context in the context stack. We'll look from top to bottom
for idx := range gui.State.ContextStack {
reversedIdx := len(gui.State.ContextStack) - 1 - idx
context := gui.State.ContextStack[reversedIdx]
if context.GetKind() == SIDE_CONTEXT {
viewName := context.GetViewName()
// unfortunate result of the fact that these are separate views, have to map explicitly
if currentCyclebleView == "commitFiles" {
if viewName == "commitFiles" {
return "commits"
}
return currentCyclebleView
return viewName
}
}
return "files" // default
}

View File

@ -44,8 +44,6 @@ func (gui *Gui) handleCommitFileSelect() error {
return err
}
gui.getCommitFilesView().FocusPoint(0, gui.State.Panels.CommitFiles.SelectedLine)
cmd := gui.OSCommand.ExecutableFromString(
gui.GitCommand.ShowCommitFileCmdStr(commitFile.Sha, commitFile.Name, false),
)
@ -57,7 +55,7 @@ func (gui *Gui) handleCommitFileSelect() error {
}
func (gui *Gui) handleSwitchToCommitsPanel(g *gocui.Gui, v *gocui.View) error {
return gui.switchFocus(v, gui.getCommitsView())
return gui.switchContext(gui.Contexts.BranchCommits.Context)
}
func (gui *Gui) handleCheckoutCommitFile(g *gocui.Gui, v *gocui.View) error {
@ -223,8 +221,8 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
}
}
gui.changeMainViewsContext("patch-building")
if err := gui.switchFocus(gui.getCommitFilesView(), gui.getMainView()); err != nil {
gui.changeMainViewsContext("patch-building") // TODO: bring into context code
if err := gui.switchContext(gui.Contexts.PatchBuilding.Context); err != nil {
return err
}
return gui.refreshPatchBuildingPanel(selectedLineIdx)
@ -241,7 +239,7 @@ func (gui *Gui) enterCommitFile(selectedLineIdx int) error {
return enterTheFile(selectedLineIdx)
},
handleClose: func() error {
return gui.switchFocus(nil, gui.getCommitFilesView())
return gui.switchContext(gui.Contexts.BranchCommits.Files.Context)
},
})
}

View File

@ -44,19 +44,19 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
}
v.Clear()
_, _ = g.SetViewOnBottom("commitMessage") // TODO: bring into context code
_ = v.SetCursor(0, 0)
_ = v.SetOrigin(0, 0)
_, _ = g.SetViewOnBottom("commitMessage")
_ = gui.switchFocus(v, gui.getFilesView())
_ = gui.returnFromContext()
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
}
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
_, _ = g.SetViewOnBottom("commitMessage")
return gui.switchFocus(v, gui.getFilesView())
_, _ = g.SetViewOnBottom("commitMessage") // TODO: bring into context code
return gui.returnFromContext()
}
func (gui *Gui) handleCommitFocused() error {
func (gui *Gui) handleCommitMessageFocused() error {
if _, err := gui.g.SetViewOnTop("commitMessage"); err != nil {
return err
}

View File

@ -54,8 +54,6 @@ func (gui *Gui) handleCommitSelect() error {
return gui.newStringTask("main", gui.Tr.SLocalize("NoCommitsThisBranch"))
}
gui.getCommitsView().FocusPoint(0, gui.State.Panels.Commits.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}
@ -104,7 +102,7 @@ func (gui *Gui) refreshCommits() error {
go func() {
_ = gui.refreshCommitsWithLimit()
if gui.g.CurrentView() == gui.getCommitFilesView() || (gui.g.CurrentView() == gui.getMainView() && gui.State.MainContext == "patch-building") {
if gui.g.CurrentView() == gui.getCommitFilesView() || (gui.currentContext().GetKey() == gui.Contexts.PatchBuilding.Context.GetKey()) {
_ = gui.refreshCommitFilesView()
}
wg.Done()
@ -528,7 +526,7 @@ func (gui *Gui) handleSwitchToCommitFilesPanel() error {
return err
}
return gui.switchFocus(gui.getCommitsView(), gui.getCommitFilesView())
return gui.switchContext(gui.Contexts.BranchCommits.Files.Context)
}
func (gui *Gui) hasCommit(commits []*commands.Commit, target string) (int, bool) {

View File

@ -94,15 +94,13 @@ func (gui *Gui) wrappedPromptConfirmationFunction(function func(string) error, r
}
func (gui *Gui) closeConfirmationPrompt(returnFocusOnClose bool) error {
view, err := gui.g.View("confirmation")
if err != nil {
view := gui.getConfirmationView()
if view == nil {
return nil // if it's already been closed we can just return
}
view.Editable = false
if returnFocusOnClose {
if err := gui.returnFocus(view); err != nil {
panic(err)
}
if err := gui.returnFromContext(); err != nil {
return err
}
gui.g.DeleteKeybinding("confirmation", gui.getKey("universal.confirm"), gocui.ModNone)
@ -164,7 +162,7 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
confirmationView.FgColor = theme.GocuiDefaultTextColor
}
gui.g.Update(func(g *gocui.Gui) error {
return gui.switchFocus(currentView, confirmationView)
return gui.switchContext(gui.Contexts.Confirmation.Context)
})
return confirmationView, nil
}

View File

@ -1,7 +1,8 @@
package gui
import (
"github.com/jesseduffield/lazygit/pkg/gui/stack"
"github.com/golang-collections/collections/stack"
"github.com/jesseduffield/gocui"
)
// changeContext is a helper function for when we want to change a 'main' context
@ -22,6 +23,27 @@ func (gui *Gui) changeMainViewsContext(context string) {
gui.State.MainContext = context
}
type Stack struct {
stack []Context
}
func (s *Stack) Push(contextKey Context) {
s.stack = append(s.stack, contextKey)
}
func (s *Stack) Pop() (Context, bool) {
if len(s.stack) == 0 {
return nil, false
}
n := len(s.stack) - 1
value := s.stack[n]
s.stack = s.stack[:n]
return value, true
}
// the context manager maintains a stack of contexts so that we can easily switch focus back and forth
type contextManager struct {
gui *Gui
stack stack.Stack
@ -33,54 +55,345 @@ func (c *contextManager) push(contextKey string) {
// push focus, pop focus.
const (
SIDE_CONTEXT int = iota
MAIN_CONTEXT
TEMPORARY_POPUP
PERSISTENT_POPUP
)
func GetKindWrapper(k int) func() int { return func() int { return k } }
type Context interface {
OnFocus() error
HandleFocus() error
HandleFocusLost() error
GetKind() int
GetViewName() string
GetKey() string
}
type SimpleContext struct {
Self Context
type BasicContext struct {
OnFocus func() error
OnFocusLost func() error
Kind int
Key string
ViewName string
}
type RemotesContext struct {
Self Context
Branches Context
func (c BasicContext) GetViewName() string {
return c.ViewName
}
type CommitsContext struct {
Self Context
Files Context
func (c BasicContext) HandleFocus() error {
return c.OnFocus()
}
func (c BasicContext) HandleFocusLost() error {
if c.OnFocusLost != nil {
return c.OnFocusLost()
}
return nil
}
func (c BasicContext) GetKind() int {
return c.Kind
}
func (c BasicContext) GetKey() string {
return c.Key
}
type SimpleContextNode struct {
Context Context
}
type RemotesContextNode struct {
Context Context
Branches SimpleContextNode
}
type CommitsContextNode struct {
Context Context
Files SimpleContextNode
}
type ContextTree struct {
Status SimpleContext
Files SimpleContext
Branches SimpleContext
Remotes RemotesContext
Tags SimpleContext
Commits CommitsContext
Stash SimpleContext
Staging SimpleContext
PatchBuilding SimpleContext
Merging SimpleContext
Menu SimpleContext
Credentials SimpleContext
Confirmation SimpleContext
CommitMessage SimpleContext
Status SimpleContextNode
Files SimpleContextNode
Menu SimpleContextNode
Branches SimpleContextNode
Remotes RemotesContextNode
Tags SimpleContextNode
BranchCommits CommitsContextNode
ReflogCommits SimpleContextNode
Stash SimpleContextNode
Staging SimpleContextNode
PatchBuilding SimpleContextNode
Merging SimpleContextNode
Credentials SimpleContextNode
Confirmation SimpleContextNode
CommitMessage SimpleContextNode
Search SimpleContextNode
}
func (gui *Gui) switchContext(c Context) error {
// push onto stack
// if we are switching to a side context, remove all other contexts in the stack
if c.GetKind() == SIDE_CONTEXT {
gui.State.ContextStack = []Context{c}
} else {
// TODO: think about other exceptional cases
gui.State.ContextStack = append(gui.State.ContextStack, c)
}
return gui.activateContext(c)
}
// switchContextToView is to be used when you don't know which context you
// want to switch to: you only know the view that you want to switch to. It will
// look up the context currently active for that view and switch to that context
func (gui *Gui) switchContextToView(viewName string) error {
return gui.switchContext(gui.State.ViewContextMap[viewName])
}
func (gui *Gui) renderContextStack() string {
result := ""
for _, context := range gui.State.ContextStack {
result += context.GetViewName() + "\n"
}
return result
}
func (gui *Gui) activateContext(c Context) error {
gui.Log.Warn(gui.renderContextStack())
if _, err := gui.g.SetCurrentView(c.GetViewName()); err != nil {
return err
}
if _, err := gui.g.SetViewOnTop(c.GetViewName()); err != nil {
return err
}
newView := gui.g.CurrentView()
gui.g.Cursor = newView.Editable
// TODO: move this logic to the context
if err := gui.renderPanelOptions(); err != nil {
return err
}
// return gui.newLineFocused(newView)
if err := c.HandleFocus(); err != nil {
return err
}
gui.State.ViewContextMap[c.GetViewName()] = c
return nil
}
func (gui *Gui) returnFromContext() error {
// TODO: add mutexes
if len(gui.State.ContextStack) == 1 {
// cannot escape from bottommost context
return nil
}
n := len(gui.State.ContextStack) - 1
currentContext := gui.State.ContextStack[n]
newContext := gui.State.ContextStack[n-1]
gui.State.ContextStack = gui.State.ContextStack[:n]
if err := currentContext.HandleFocusLost(); err != nil {
return err
}
return gui.activateContext(newContext)
}
func (gui *Gui) currentContext() Context {
return gui.State.ContextStack[len(gui.State.ContextStack)-1]
}
func (gui *Gui) createContextTree() {
gui.State.Contexts = ContextTree{
Files: SimpleContext{
Self: gui.filesListView(),
gui.Contexts = ContextTree{
Status: SimpleContextNode{
Context: BasicContext{
OnFocus: gui.handleStatusSelect,
Kind: SIDE_CONTEXT,
ViewName: "status",
},
},
Files: SimpleContextNode{
Context: gui.filesListView(),
},
Menu: SimpleContextNode{
Context: gui.menuListView(),
},
Remotes: RemotesContextNode{
Context: gui.remotesListView(),
Branches: SimpleContextNode{
Context: gui.remoteBranchesListView(),
},
},
BranchCommits: CommitsContextNode{
Context: gui.branchCommitsListView(),
Files: SimpleContextNode{
Context: gui.commitFilesListView(),
},
},
ReflogCommits: SimpleContextNode{
Context: gui.reflogCommitsListView(),
},
Branches: SimpleContextNode{
Context: gui.branchesListView(),
},
Tags: SimpleContextNode{
Context: gui.tagsListView(),
},
Stash: SimpleContextNode{
Context: gui.stashListView(),
},
Staging: SimpleContextNode{
Context: BasicContext{
// TODO: think about different situations where this arises
OnFocus: func() error {
return gui.refreshStagingPanel(false, -1)
},
Kind: MAIN_CONTEXT,
ViewName: "main",
Key: "staging",
},
},
PatchBuilding: SimpleContextNode{
Context: BasicContext{
// TODO: think about different situations where this arises
OnFocus: func() error {
return gui.refreshPatchBuildingPanel(-1)
},
Kind: MAIN_CONTEXT,
ViewName: "main",
Key: "patch-building",
},
},
Merging: SimpleContextNode{
Context: BasicContext{
// TODO: think about different situations where this arises
OnFocus: func() error {
return gui.refreshMergePanel()
},
Kind: MAIN_CONTEXT,
ViewName: "main",
Key: "merging",
},
},
Credentials: SimpleContextNode{
Context: BasicContext{
OnFocus: func() error { return gui.handleCredentialsViewFocused() },
Kind: PERSISTENT_POPUP,
ViewName: "credentials",
Key: "credentials",
},
},
Confirmation: SimpleContextNode{
Context: BasicContext{
OnFocus: func() error { return nil },
Kind: TEMPORARY_POPUP,
ViewName: "confirmation",
Key: "confirmation",
},
},
CommitMessage: SimpleContextNode{
Context: BasicContext{
OnFocus: func() error { return gui.handleCommitMessageFocused() },
Kind: PERSISTENT_POPUP,
ViewName: "commitMessage",
Key: "commit-message",
},
},
Search: SimpleContextNode{
Context: BasicContext{
OnFocus: func() error { return nil },
Kind: PERSISTENT_POPUP,
ViewName: "search",
Key: "search",
},
},
}
gui.State.ViewContextMap = map[string]Context{
"status": gui.Contexts.Status.Context,
"files": gui.Contexts.Files.Context,
"branches": gui.Contexts.Branches.Context,
"commits": gui.Contexts.BranchCommits.Context,
"stash": gui.Contexts.Stash.Context,
"menu": gui.Contexts.Menu.Context,
"confirmation": gui.Contexts.Confirmation.Context,
"credentials": gui.Contexts.Credentials.Context,
"commitMessage": gui.Contexts.CommitMessage.Context,
"main": gui.Contexts.Staging.Context,
}
}
// func (c *contextManager) pop() (string, bool) {
// value, ok := c.stack.Pop()
// getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var previousView *gocui.View
return func(g *gocui.Gui) error {
newView := gui.g.CurrentView()
if err := gui.onFocusChange(); err != nil {
return err
}
// for now we don't consider losing focus to a popup panel as actually losing focus
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
if err := gui.onViewFocusLost(previousView, newView); err != nil {
return err
}
// if !ok {
// // bottom of the stack, let's go to the default context: the files context
// c.gui.switchFocus(nil, newView)
// }
// }
if err := gui.onViewFocus(newView); err != nil {
return err
}
previousView = newView
}
return nil
}
}
func (gui *Gui) onFocusChange() error {
currentView := gui.g.CurrentView()
for _, view := range gui.g.Views() {
view.Highlight = view.Name() != "main" && view == currentView
}
return nil
}
func (gui *Gui) onViewFocusLost(v *gocui.View, newView *gocui.View) error {
if v == nil {
return nil
}
if v.IsSearching() && newView.Name() != "search" {
if err := gui.onSearchEscape(); err != nil {
return err
}
}
if v.Name() == "main" {
// if we have lost focus to a first-class panel, we need to do some cleanup
gui.changeMainViewsContext("normal")
}
gui.Log.Info(v.Name() + " focus lost")
return nil
}
func (gui *Gui) onViewFocus(newView *gocui.View) error {
gui.setViewAsActiveForWindow(newView.Name())
return nil
}

View File

@ -20,10 +20,11 @@ func (gui *Gui) promptUserForCredential(passOrUname string) string {
credentialsView.Title = gui.Tr.SLocalize("CredentialsPassword")
credentialsView.Mask = '*'
}
err := gui.switchFocus(gui.g.CurrentView(), credentialsView)
if err != nil {
if err := gui.switchContext(gui.Contexts.Credentials.Context); err != nil {
return err
}
gui.RenderCommitLength()
return nil
})
@ -38,15 +39,11 @@ func (gui *Gui) handleSubmitCredential(g *gocui.Gui, v *gocui.View) error {
gui.credentials <- message
v.Clear()
_ = v.SetCursor(0, 0)
_, _ = g.SetViewOnBottom("credentials")
nextView, err := gui.g.View("confirmation")
if err != nil {
nextView = gui.getFilesView()
}
err = gui.switchFocus(nil, nextView)
if err != nil {
_, _ = g.SetViewOnBottom("credentials") // TODO: move to context code
if err := gui.returnFromContext(); err != nil {
return err
}
return gui.refreshSidePanels(refreshOptions{})
}
@ -57,7 +54,7 @@ func (gui *Gui) handleCloseCredentialsView(g *gocui.Gui, v *gocui.View) error {
}
gui.credentials <- ""
return gui.switchFocus(nil, gui.getFilesView())
return gui.returnFromContext()
}
func (gui *Gui) handleCredentialsViewFocused() error {

View File

@ -171,11 +171,10 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
if file.HasMergeConflicts {
return gui.createErrorPanel(gui.Tr.SLocalize("FileStagingRequirements"))
}
gui.changeMainViewsContext("staging")
if err := gui.switchFocus(gui.getFilesView(), gui.getMainView()); err != nil {
return err
}
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx)
gui.changeMainViewsContext("staging") // TODO: move into context code
gui.switchContext(gui.Contexts.Staging.Context)
return gui.refreshStagingPanel(forceSecondaryFocused, selectedLineIdx) // TODO: check if this is broken, try moving into context code
}
func (gui *Gui) handleFilePress() error {
@ -310,11 +309,7 @@ func (gui *Gui) handleCommitPress() error {
}
gui.g.Update(func(g *gocui.Gui) error {
if _, err := g.SetViewOnTop("commitMessage"); err != nil {
return err
}
if err := gui.switchFocus(gui.getFilesView(), commitMessageView); err != nil {
if err := gui.switchContext(gui.Contexts.CommitMessage.Context); err != nil {
return err
}
@ -596,11 +591,8 @@ func (gui *Gui) handleSwitchToMerge() error {
if !file.HasInlineMergeConflicts {
return gui.createErrorPanel(gui.Tr.SLocalize("FileNoMergeCons"))
}
gui.changeMainViewsContext("merging")
if err := gui.switchFocus(gui.g.CurrentView(), gui.getMainView()); err != nil {
return err
}
return gui.refreshMergePanel()
gui.changeMainViewsContext("merging") // TODO: move into context code
return gui.switchContext(gui.Contexts.Merging.Context)
}
func (gui *Gui) openFile(filename string) error {

View File

@ -97,6 +97,7 @@ type Gui struct {
// when lazygit is opened outside a git directory we want to open to the most
// recent repo with the recent repos popup showing
showRecentRepos bool
Contexts ContextTree
}
// for now the staging panel state, unlike the other panel states, is going to be
@ -237,7 +238,15 @@ type guiState struct {
FilterPath string // the filename that gets passed to git log
Diff DiffState
Contexts ContextTree
ContextStack []Context
ViewContextMap map[string]Context
// WindowViewNameMap is a mapping of windows to the current view of that window.
// Currently the only case where the distinction between a window and a view
// matters is with the commits view and the commitFiles view which both appear
// in the same place (and thus constitute the 'commits' window).
// If a window contains only one view, it shares the same name as the view.
WindowViewNameMap map[string]string
}
func (gui *Gui) resetState() {

View File

@ -1393,17 +1393,17 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
bindings = append(bindings, []*Binding{
{ViewName: viewName, Key: gui.getKey("universal.togglePanel"), Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gui.getKey("universal.prevBlock"), Modifier: gocui.ModNone, Handler: gui.previousView},
{ViewName: viewName, Key: gui.getKey("universal.nextBlock"), Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gui.getKey("universal.prevBlock-alt"), Modifier: gocui.ModNone, Handler: gui.previousView},
{ViewName: viewName, Key: gui.getKey("universal.nextBlock-alt"), Modifier: gocui.ModNone, Handler: gui.nextView},
{ViewName: viewName, Key: gui.getKey("universal.togglePanel"), Modifier: gocui.ModNone, Handler: gui.wrappedHandler(gui.nextSideWindow)},
{ViewName: viewName, Key: gui.getKey("universal.prevBlock"), Modifier: gocui.ModNone, Handler: gui.wrappedHandler(gui.previousSideWindow)},
{ViewName: viewName, Key: gui.getKey("universal.nextBlock"), Modifier: gocui.ModNone, Handler: gui.wrappedHandler(gui.nextSideWindow)},
{ViewName: viewName, Key: gui.getKey("universal.prevBlock-alt"), Modifier: gocui.ModNone, Handler: gui.wrappedHandler(gui.previousSideWindow)},
{ViewName: viewName, Key: gui.getKey("universal.nextBlock-alt"), Modifier: gocui.ModNone, Handler: gui.wrappedHandler(gui.nextSideWindow)},
}...)
}
// Appends keybindings to jump to a particular sideView using numbers
for i, viewName := range []string{"status", "files", "branches", "commits", "stash"} {
bindings = append(bindings, &Binding{ViewName: "", Key: rune(i+1) + '0', Modifier: gocui.ModNone, Handler: gui.goToSideView(viewName)})
for i, window := range []string{"status", "files", "branches", "commits", "stash"} {
bindings = append(bindings, &Binding{ViewName: "", Key: rune(i+1) + '0', Modifier: gocui.ModNone, Handler: gui.goToSideWindow(window)})
}
for _, listView := range gui.getListViews() {

View File

@ -12,68 +12,6 @@ import (
const SEARCH_PREFIX = "search: "
const INFO_SECTION_PADDING = " "
// getFocusLayout returns a manager function for when view gain and lose focus
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
var previousView *gocui.View
return func(g *gocui.Gui) error {
newView := gui.g.CurrentView()
if err := gui.onFocusChange(); err != nil {
return err
}
// for now we don't consider losing focus to a popup panel as actually losing focus
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
if err := gui.onFocusLost(previousView, newView); err != nil {
return err
}
if err := gui.onFocus(newView); err != nil {
return err
}
previousView = newView
}
return nil
}
}
func (gui *Gui) onFocusChange() error {
currentView := gui.g.CurrentView()
for _, view := range gui.g.Views() {
view.Highlight = view == currentView
}
return nil
}
func (gui *Gui) onFocusLost(v *gocui.View, newView *gocui.View) error {
if v == nil {
return nil
}
if v.IsSearching() && newView.Name() != "search" {
if err := gui.onSearchEscape(); err != nil {
return err
}
}
switch v.Name() {
case "main":
// if we have lost focus to a first-class panel, we need to do some cleanup
gui.changeMainViewsContext("normal")
case "commitFiles":
if gui.State.MainContext != "patch-building" {
if _, err := gui.g.SetViewOnBottom(v.Name()); err != nil {
return err
}
}
}
gui.Log.Info(v.Name() + " focus lost")
return nil
}
func (gui *Gui) onFocus(v *gocui.View) error {
if v == nil {
return nil
}
gui.Log.Info(v.Name() + " focus gained")
return nil
}
func (gui *Gui) informationStr() string {
if gui.inDiffMode() {
return utils.ColoredString(fmt.Sprintf("%s %s %s", gui.Tr.SLocalize("showingGitDiff"), "git diff "+gui.diffStr(), utils.ColoredString(gui.Tr.SLocalize("(reset)"), color.Underline)), color.FgMagenta)
@ -329,15 +267,12 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
if gui.g.CurrentView() == nil {
initialView := gui.getFilesView()
initialContext := gui.Contexts.Files.Context
if gui.inFilterMode() {
initialView = gui.getCommitsView()
}
if _, err := gui.g.SetCurrentView(initialView.Name()); err != nil {
return err
initialContext = gui.Contexts.BranchCommits.Context
}
if err := gui.switchFocus(nil, initialView); err != nil {
if err := gui.switchContext(initialContext); err != nil {
return err
}
}
@ -347,7 +282,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
lineCount int
view *gocui.View
context string
listView *listView
listView *ListView
}
listViewStates := []listViewState{
@ -397,6 +332,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
func (gui *Gui) onInitialViewsCreation() error {
gui.createContextTree()
gui.switchContext(gui.Contexts.Files.Context)
gui.changeMainViewsContext("normal")
gui.getBranchesView().Context = "local-branches"

View File

@ -234,7 +234,7 @@ func (gui *Gui) refreshMainView() error {
var includedLineIndices []int
// I'd prefer not to have knowledge of contexts using this file but I'm not sure
// how to get around this
if gui.State.MainContext == "patch-building" {
if gui.currentContext().GetKey() == gui.Contexts.PatchBuilding.Context.GetKey() {
filename := gui.getSelectedCommitFileName()
includedLineIndices = gui.GitCommand.PatchManager.GetFileIncLineIndices(filename)
}

View File

@ -2,27 +2,54 @@ package gui
import "github.com/jesseduffield/gocui"
type listView struct {
type ListView struct {
ViewName string
Context string
GetItemsLength func() int
GetSelectedLineIdxPtr func() *int
OnFocus func() error
OnFocusLost func() error
OnItemSelect func() error
OnClickSelectedItem func() error
Gui *Gui
RendersToMainView bool
Kind int
Key string
}
func (lv *listView) handlePrevLine(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) GetKey() string {
return lv.Key
}
func (lv *ListView) GetKind() int {
return lv.Kind
}
func (lv *ListView) GetViewName() string {
return lv.ViewName
}
func (lv *ListView) HandleFocusLost() error {
if lv.OnFocusLost != nil {
return lv.OnFocusLost()
}
return nil
}
func (lv *ListView) HandleFocus() error {
return lv.OnFocus()
}
func (lv *ListView) handlePrevLine(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(-1)
}
func (lv *listView) handleNextLine(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handleNextLine(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(1)
}
func (lv *listView) handleLineChange(change int) error {
func (lv *ListView) handleLineChange(change int) error {
if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil
}
@ -50,7 +77,7 @@ func (lv *listView) handleLineChange(change int) error {
return nil
}
func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
view, err := lv.Gui.g.View(lv.ViewName)
if err != nil {
return nil
@ -63,15 +90,15 @@ func (lv *listView) handleNextPage(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(delta)
}
func (lv *listView) handleGotoTop(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handleGotoTop(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(-lv.GetItemsLength())
}
func (lv *listView) handleGotoBottom(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handleGotoBottom(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(lv.GetItemsLength())
}
func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
view, err := lv.Gui.g.View(lv.ViewName)
if err != nil {
return nil
@ -84,7 +111,7 @@ func (lv *listView) handlePrevPage(g *gocui.Gui, v *gocui.View) error {
return lv.handleLineChange(-delta)
}
func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
func (lv *ListView) handleClick(g *gocui.Gui, v *gocui.View) error {
if !lv.Gui.isPopupPanel(lv.ViewName) && lv.Gui.popupPanelFocused() {
return nil
}
@ -94,12 +121,12 @@ func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
newSelectedLineIdx := v.SelectedLineIdx()
// we need to focus the view
if err := lv.Gui.switchFocus(nil, v); err != nil {
if err := lv.Gui.switchContext(lv); err != nil {
return err
}
if newSelectedLineIdx > lv.GetItemsLength()-1 {
return lv.OnFocus()
return nil
}
*selectedLineIdxPtr = newSelectedLineIdx
@ -114,7 +141,7 @@ func (lv *listView) handleClick(g *gocui.Gui, v *gocui.View) error {
return nil
}
func (lv *listView) onSearchSelect(selectedLineIdx int) error {
func (lv *ListView) onSearchSelect(selectedLineIdx int) error {
*lv.GetSelectedLineIdxPtr() = selectedLineIdx
if lv.OnItemSelect != nil {
return lv.OnItemSelect()
@ -122,8 +149,8 @@ func (lv *listView) onSearchSelect(selectedLineIdx int) error {
return nil
}
func (gui *Gui) menuListView() *listView {
return &listView{
func (gui *Gui) menuListView() *ListView {
return &ListView{
ViewName: "menu",
GetItemsLength: func() int { return gui.getMenuView().LinesHeight() },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Menu.SelectedLine },
@ -133,11 +160,13 @@ func (gui *Gui) menuListView() *listView {
OnClickSelectedItem: func() error { return gui.State.Panels.Menu.OnPress(gui.g, nil) },
Gui: gui,
RendersToMainView: false,
Kind: PERSISTENT_POPUP,
Key: "menu",
}
}
func (gui *Gui) filesListView() *listView {
return &listView{
func (gui *Gui) filesListView() *ListView {
return &ListView{
ViewName: "files",
GetItemsLength: func() int { return len(gui.State.Files) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Files.SelectedLine },
@ -146,11 +175,13 @@ func (gui *Gui) filesListView() *listView {
OnClickSelectedItem: gui.handleFilePress,
Gui: gui,
RendersToMainView: false,
Kind: SIDE_CONTEXT,
Key: "files",
}
}
func (gui *Gui) branchesListView() *listView {
return &listView{
func (gui *Gui) branchesListView() *ListView {
return &ListView{
ViewName: "branches",
Context: "local-branches",
GetItemsLength: func() int { return len(gui.State.Branches) },
@ -159,11 +190,13 @@ func (gui *Gui) branchesListView() *listView {
OnItemSelect: gui.handleBranchSelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
Key: "menu",
}
}
func (gui *Gui) remotesListView() *listView {
return &listView{
func (gui *Gui) remotesListView() *ListView {
return &ListView{
ViewName: "branches",
Context: "remotes",
GetItemsLength: func() int { return len(gui.State.Remotes) },
@ -173,11 +206,12 @@ func (gui *Gui) remotesListView() *listView {
OnClickSelectedItem: gui.handleRemoteEnter,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) remoteBranchesListView() *listView {
return &listView{
func (gui *Gui) remoteBranchesListView() *ListView {
return &ListView{
ViewName: "branches",
Context: "remote-branches",
GetItemsLength: func() int { return len(gui.State.RemoteBranches) },
@ -186,11 +220,12 @@ func (gui *Gui) remoteBranchesListView() *listView {
OnItemSelect: gui.handleRemoteBranchSelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) tagsListView() *listView {
return &listView{
func (gui *Gui) tagsListView() *ListView {
return &ListView{
ViewName: "branches",
Context: "tags",
GetItemsLength: func() int { return len(gui.State.Tags) },
@ -199,11 +234,12 @@ func (gui *Gui) tagsListView() *listView {
OnItemSelect: gui.handleTagSelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) branchCommitsListView() *listView {
return &listView{
func (gui *Gui) branchCommitsListView() *ListView {
return &ListView{
ViewName: "commits",
Context: "branch-commits",
GetItemsLength: func() int { return len(gui.State.Commits) },
@ -213,11 +249,12 @@ func (gui *Gui) branchCommitsListView() *listView {
OnClickSelectedItem: gui.handleSwitchToCommitFilesPanel,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) reflogCommitsListView() *listView {
return &listView{
func (gui *Gui) reflogCommitsListView() *ListView {
return &ListView{
ViewName: "commits",
Context: "reflog-commits",
GetItemsLength: func() int { return len(gui.State.FilteredReflogCommits) },
@ -226,11 +263,12 @@ func (gui *Gui) reflogCommitsListView() *listView {
OnItemSelect: gui.handleReflogCommitSelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) stashListView() *listView {
return &listView{
func (gui *Gui) stashListView() *ListView {
return &ListView{
ViewName: "stash",
GetItemsLength: func() int { return len(gui.State.StashEntries) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Stash.SelectedLine },
@ -238,11 +276,12 @@ func (gui *Gui) stashListView() *listView {
OnItemSelect: gui.handleStashEntrySelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) commitFilesListView() *listView {
return &listView{
func (gui *Gui) commitFilesListView() *ListView {
return &ListView{
ViewName: "commitFiles",
GetItemsLength: func() int { return len(gui.State.CommitFiles) },
GetSelectedLineIdxPtr: func() *int { return &gui.State.Panels.CommitFiles.SelectedLine },
@ -250,11 +289,12 @@ func (gui *Gui) commitFilesListView() *listView {
OnItemSelect: gui.handleCommitFileSelect,
Gui: gui,
RendersToMainView: true,
Kind: SIDE_CONTEXT,
}
}
func (gui *Gui) getListViews() []*listView {
return []*listView{
func (gui *Gui) getListViews() []*ListView {
return []*ListView{
gui.menuListView(),
gui.filesListView(),
gui.branchesListView(),

View File

@ -17,7 +17,6 @@ type menuItem struct {
// list panel functions
func (gui *Gui) handleMenuSelect() error {
gui.getMenuView().FocusPoint(0, gui.State.Panels.Menu.SelectedLine)
return nil
}
@ -46,7 +45,7 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error {
if err != nil {
return err
}
return gui.returnFocus(v)
return gui.returnFromContext()
}
type createMenuOptions struct {
@ -84,8 +83,6 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
menuView.ContainsList = true
menuView.Clear()
menuView.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
gui.State.Panels.Menu.SelectedLine = selectedLine
menuView.FocusPoint(0, selectedLine)
return nil
}))
fmt.Fprint(menuView, list)
@ -103,7 +100,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
}
}
return gui.returnFocus(menuView)
return gui.returnFromContext()
}
gui.State.Panels.Menu.OnPress = wrappedHandlePress
@ -117,13 +114,7 @@ func (gui *Gui) createMenu(title string, items []*menuItem, createMenuOptions cr
}
gui.g.Update(func(g *gocui.Gui) error {
if _, err := gui.g.View("menu"); err == nil {
if _, err := g.SetViewOnTop("menu"); err != nil {
return err
}
}
currentView := gui.g.CurrentView()
return gui.switchFocus(currentView, menuView)
return gui.switchContext(gui.Contexts.Menu.Context)
})
return nil
}

View File

@ -308,7 +308,7 @@ func (gui *Gui) handleEscapeMerge() error {
// it's possible this method won't be called from the merging view so we need to
// ensure we only 'return' focus if we already have it
if gui.g.CurrentView() == gui.getMainView() {
return gui.switchFocus(gui.getMainView(), gui.getFilesView())
return gui.switchContext(gui.Contexts.Files.Context)
}
return nil
}

View File

@ -83,7 +83,7 @@ func (gui *Gui) handleEscapePatchBuildingPanel() error {
gui.State.SplitMainPanel = false
}
return gui.switchFocus(nil, gui.getCommitFilesView())
return gui.switchContext(gui.Contexts.BranchCommits.Files.Context)
}
func (gui *Gui) refreshSecondaryPatchPanel() error {

View File

@ -35,8 +35,6 @@ func (gui *Gui) handleReflogCommitSelect() error {
if commit == nil {
return gui.newStringTask("main", "No reflog history")
}
gui.getCommitsView().FocusPoint(0, gui.State.Panels.ReflogCommits.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}

View File

@ -37,8 +37,6 @@ func (gui *Gui) handleRemoteBranchSelect() error {
return gui.newStringTask("main", "No branches for this remote")
}
gui.getBranchesView().FocusPoint(0, gui.State.Panels.RemoteBranches.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}

View File

@ -39,8 +39,6 @@ func (gui *Gui) handleRemoteSelect() error {
if remote == nil {
return gui.newStringTask("main", "No remotes")
}
gui.getBranchesView().FocusPoint(0, gui.State.Panels.Remotes.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}

View File

@ -12,7 +12,7 @@ func (gui *Gui) handleOpenSearch(g *gocui.Gui, v *gocui.View) error {
gui.State.Searching.isSearching = true
gui.State.Searching.view = v
gui.renderString("search", "")
if err := gui.switchFocus(v, gui.getSearchView()); err != nil {
if err := gui.switchContext(gui.Contexts.Search.Context); err != nil {
return err
}
@ -21,7 +21,7 @@ func (gui *Gui) handleOpenSearch(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleSearch(g *gocui.Gui, v *gocui.View) error {
gui.State.Searching.searchString = gui.getSearchView().Buffer()
if err := gui.switchFocus(nil, gui.State.Searching.view); err != nil {
if err := gui.switchContextToView(gui.State.Searching.view.Name()); err != nil {
return err
}
@ -84,7 +84,7 @@ func (gui *Gui) onSearchEscape() error {
}
func (gui *Gui) handleSearchEscape(g *gocui.Gui, v *gocui.View) error {
if err := gui.switchFocus(nil, gui.State.Searching.view); err != nil {
if err := gui.switchContextToView(gui.State.Searching.view.Name()); err != nil {
return err
}

66
pkg/gui/side_window.go Normal file
View File

@ -0,0 +1,66 @@
package gui
import "github.com/jesseduffield/gocui"
func (gui *Gui) nextSideWindow() error {
windows := gui.getCyclableWindows()
currentWindow := gui.currentWindow()
var newWindow string
if currentWindow == "" || currentWindow == windows[len(windows)-1] {
newWindow = windows[0]
} else {
for i := range windows {
if currentWindow == windows[i] {
newWindow = windows[i+1]
break
}
if i == len(windows)-1 {
return nil
}
}
}
if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err
}
viewName := gui.getViewNameForWindow(newWindow)
return gui.switchContext(gui.State.ViewContextMap[viewName])
}
func (gui *Gui) previousSideWindow() error {
windows := gui.getCyclableWindows()
currentWindow := gui.currentWindow()
var newWindow string
if currentWindow == "" || currentWindow == windows[0] {
newWindow = windows[len(windows)-1]
} else {
for i := range windows {
if currentWindow == windows[i] {
newWindow = windows[i-1]
break
}
if i == len(windows)-1 {
return nil
}
}
}
if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err
}
viewName := gui.getViewNameForWindow(newWindow)
return gui.switchContext(gui.State.ViewContextMap[viewName])
}
func (gui *Gui) goToSideWindow(sideViewName string) func(g *gocui.Gui, v *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
err := gui.closePopupPanels()
if err != nil {
gui.Log.Error(err)
return nil
}
return gui.switchContext(gui.State.ViewContextMap[sideViewName])
}
}

View File

@ -1,21 +0,0 @@
package stack
type Stack struct {
stack []string
}
func (s *Stack) Push(contextKey string) {
s.stack = append(s.stack, contextKey)
}
func (s *Stack) Pop() (string, bool) {
if len(s.stack) == 0 {
return "", false
}
n := len(s.stack) - 1
value := s.stack[n]
s.stack = s.stack[:n]
return value, true
}

View File

@ -12,12 +12,12 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
state := gui.State.Panels.LineByLine
// We need to force focus here because the confirmation panel for safely staging lines does not return focus automatically.
// This is because if we tell it to return focus it will unconditionally return it to the main panel which may not be what we want
// e.g. in the event that there's nothing left to stage.
if err := gui.switchFocus(nil, gui.getMainView()); err != nil {
return err
}
// // We need to force focus here because the confirmation panel for safely staging lines does not return focus automatically.
// // This is because if we tell it to return focus it will unconditionally return it to the main panel which may not be what we want
// // e.g. in the event that there's nothing left to stage.
// if err := gui.switchContext(nil, gui.getMainView()); err != nil {
// return err
// }
file, err := gui.getSelectedFile()
if err != nil {
@ -96,7 +96,7 @@ func (gui *Gui) handleTogglePanel(g *gocui.Gui, v *gocui.View) error {
func (gui *Gui) handleStagingEscape() error {
gui.handleEscapeLineByLinePanel()
return gui.switchFocus(nil, gui.getFilesView())
return gui.switchContext(gui.Contexts.Files.Context)
}
func (gui *Gui) handleToggleStagedSelection(g *gocui.Gui, v *gocui.View) error {

View File

@ -34,8 +34,6 @@ func (gui *Gui) handleStashEntrySelect() error {
if stashEntry == nil {
return gui.newStringTask("main", gui.Tr.SLocalize("NoStashEntries"))
}
gui.getStashView().FocusPoint(0, gui.State.Panels.Stash.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()
}

View File

@ -30,7 +30,6 @@ func (gui *Gui) handleTagSelect() error {
if tag == nil {
return gui.newStringTask("main", "No tags")
}
gui.getBranchesView().FocusPoint(0, gui.State.Panels.Tags.SelectedLine)
if gui.inDiffMode() {
return gui.renderDiff()

View File

@ -6,13 +6,12 @@ import (
"strings"
"sync"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/spkg/bom"
)
func (gui *Gui) getCyclableViews() []string {
func (gui *Gui) getCyclableWindows() []string {
return []string{"status", "files", "branches", "commits", "stash"}
}
@ -140,157 +139,6 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
return nil
}
func (gui *Gui) nextView(g *gocui.Gui, v *gocui.View) error {
var focusedViewName string
cyclableViews := gui.getCyclableViews()
if v == nil || v.Name() == cyclableViews[len(cyclableViews)-1] {
focusedViewName = cyclableViews[0]
} else {
// if we're in the commitFiles view we'll act like we're in the commits view
viewName := v.Name()
if viewName == "commitFiles" {
viewName = "commits"
}
for i := range cyclableViews {
if viewName == cyclableViews[i] {
focusedViewName = cyclableViews[i+1]
break
}
if i == len(cyclableViews)-1 {
message := gui.Tr.TemplateLocalize(
"IssntListOfViews",
Teml{
"name": viewName,
},
)
gui.Log.Info(message)
return nil
}
}
}
focusedView, err := g.View(focusedViewName)
if err != nil {
panic(err)
}
if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err
}
return gui.switchFocus(v, focusedView)
}
func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error {
cyclableViews := gui.getCyclableViews()
var focusedViewName string
if v == nil || v.Name() == cyclableViews[0] {
focusedViewName = cyclableViews[len(cyclableViews)-1]
} else {
// if we're in the commitFiles view we'll act like we're in the commits view
viewName := v.Name()
if viewName == "commitFiles" {
viewName = "commits"
}
for i := range cyclableViews {
if viewName == cyclableViews[i] {
focusedViewName = cyclableViews[i-1] // TODO: make this work properly
break
}
if i == len(cyclableViews)-1 {
message := gui.Tr.TemplateLocalize(
"IssntListOfViews",
Teml{
"name": viewName,
},
)
gui.Log.Info(message)
return nil
}
}
}
focusedView, err := g.View(focusedViewName)
if err != nil {
panic(err)
}
if err := gui.resetOrigin(gui.getMainView()); err != nil {
return err
}
return gui.switchFocus(v, focusedView)
}
func (gui *Gui) newLineFocused(v *gocui.View) error {
switch v.Name() {
case "menu":
return gui.handleMenuSelect()
case "status":
return gui.handleStatusSelect()
case "files":
return gui.focusAndSelectFile()
case "branches":
branchesView := gui.getBranchesView()
switch branchesView.Context {
case "local-branches":
return gui.handleBranchSelect()
case "remotes":
return gui.handleRemoteSelect()
case "remote-branches":
return gui.handleRemoteBranchSelect()
case "tags":
return gui.handleTagSelect()
default:
return errors.New("unknown branches panel context: " + branchesView.Context)
}
case "commits":
return gui.handleCommitSelect()
case "commitFiles":
return gui.handleCommitFileSelect()
case "stash":
return gui.handleStashEntrySelect()
case "confirmation":
return nil
case "commitMessage":
return gui.handleCommitFocused()
case "credentials":
return gui.handleCredentialsViewFocused()
case "main":
if gui.State.MainContext == "merging" {
return gui.refreshMergePanel()
}
v.Highlight = false
return nil
case "search":
return nil
default:
panic(gui.Tr.SLocalize("NoViewMachingNewLineFocusedSwitchStatement"))
}
}
func (gui *Gui) returnFocus(v *gocui.View) error {
previousView, err := gui.g.View(gui.State.PreviousView)
if err != nil {
// always fall back to files view if there's no 'previous' view stored
previousView, err = gui.g.View("files")
if err != nil {
gui.Log.Error(err)
}
}
return gui.switchFocus(v, previousView)
}
func (gui *Gui) goToSideView(sideViewName string) func(g *gocui.Gui, v *gocui.View) error {
return func(g *gocui.Gui, v *gocui.View) error {
view, err := g.View(sideViewName)
if err != nil {
gui.Log.Error(err)
return nil
}
err = gui.closePopupPanels()
if err != nil {
gui.Log.Error(err)
return nil
}
return gui.switchFocus(nil, view)
}
}
func (gui *Gui) closePopupPanels() error {
gui.onNewPopupPanel()
err := gui.closeConfirmationPrompt(true)
@ -301,39 +149,6 @@ func (gui *Gui) closePopupPanels() error {
return nil
}
// pass in oldView = nil if you don't want to be able to return to your old view
// TODO: move some of this logic into our onFocusLost and onFocus hooks
func (gui *Gui) switchFocus(oldView, newView *gocui.View) error {
// we assume we'll never want to return focus to a popup panel i.e.
// we should never stack popup panels
if oldView != nil && !gui.isPopupPanel(oldView.Name()) {
gui.State.PreviousView = oldView.Name()
}
gui.Log.Info("setting highlight to true for view" + newView.Name())
message := gui.Tr.TemplateLocalize(
"newFocusedViewIs",
Teml{
"newFocusedView": newView.Name(),
},
)
gui.Log.Info(message)
if _, err := gui.g.SetCurrentView(newView.Name()); err != nil {
return err
}
if _, err := gui.g.SetViewOnTop(newView.Name()); err != nil {
return err
}
gui.g.Cursor = newView.Editable
if err := gui.renderPanelOptions(); err != nil {
return err
}
return gui.newLineFocused(newView)
}
func (gui *Gui) resetOrigin(v *gocui.View) error {
_ = v.SetCursor(0, 0)
return v.SetOrigin(0, 0)
@ -442,6 +257,11 @@ func (gui *Gui) getStatusView() *gocui.View {
return v
}
func (gui *Gui) getConfirmationView() *gocui.View {
v, _ := gui.g.View("confirmation")
return v
}
func (gui *Gui) trimmedContent(v *gocui.View) string {
return strings.TrimSpace(v.Buffer())
}

37
pkg/gui/window.go Normal file
View File

@ -0,0 +1,37 @@
package gui
// A window refers to a place on the screen which can hold one or more views.
// A view is a box that renders content, and within a window only one view will
// appear at a time. When a view appears within a window, it occupies the whole
// space. Right now most windows are 1:1 with views, except for commitFiles which
// is a view belonging to the 'commits' window, alongside the 'commits' view.
func (gui *Gui) getViewNameForWindow(window string) string {
viewName, ok := gui.State.WindowViewNameMap[window]
if !ok {
return window
}
return viewName
}
func (gui *Gui) getWindowForViewName(viewName string) string {
// should soft-code this
if viewName == "commitFiles" {
return "commits"
}
return viewName
}
func (gui *Gui) setViewAsActiveForWindow(viewName string) {
if gui.State.WindowViewNameMap == nil {
gui.State.WindowViewNameMap = map[string]string{}
}
gui.State.WindowViewNameMap[gui.getWindowForViewName(viewName)] = viewName
}
func (gui *Gui) currentWindow() string {
return gui.getWindowForViewName(gui.currentViewName())
}

View File

@ -397,9 +397,6 @@ func addDutch(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "IssntListOfViews",
Other: "{{.name}} is niet in de lijst van weergaves",
}, &i18n.Message{
ID: "NoViewMachingNewLineFocusedSwitchStatement",
Other: "Er machen geen weergave met de newLineFocused switch declaratie",
}, &i18n.Message{
ID: "newFocusedViewIs",
Other: "nieuw gefocussed weergave is {{.newFocusedView}}",

View File

@ -405,9 +405,6 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "IssntListOfViews",
Other: "{{.name}} is not in the list of views",
}, &i18n.Message{
ID: "NoViewMachingNewLineFocusedSwitchStatement",
Other: "No view matching newLineFocused switch statement",
}, &i18n.Message{
ID: "newFocusedViewIs",
Other: "new focused view is {{.newFocusedView}}",

View File

@ -320,9 +320,6 @@ func addPolish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "IssntListOfViews",
Other: "{{.name}} nie jest na liście widoków",
}, &i18n.Message{
ID: "NoViewMachingNewLineFocusedSwitchStatement",
Other: "Brak widoku pasującego do instrukcji przełączania newLineFocused",
}, &i18n.Message{
ID: "newFocusedViewIs",
Other: "nowy skupiony widok to {{.newFocusedView}}",