mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-17 22:32:58 +02:00
add half and fullscreen modes
This commit is contained in:
parent
52b5a6410c
commit
fdb543fa7d
@ -75,6 +75,8 @@ Default path for the config file:
|
|||||||
createPatchOptionsMenu: '<c-p>'
|
createPatchOptionsMenu: '<c-p>'
|
||||||
nextTab: ']'
|
nextTab: ']'
|
||||||
prevTab: '['
|
prevTab: '['
|
||||||
|
nextScreenMode: '+'
|
||||||
|
prevScreenMode: '_'
|
||||||
status:
|
status:
|
||||||
checkForUpdate: 'u'
|
checkForUpdate: 'u'
|
||||||
recentRepos: '<enter>'
|
recentRepos: '<enter>'
|
||||||
|
@ -308,6 +308,8 @@ keybinding:
|
|||||||
createPatchOptionsMenu: '<c-p>'
|
createPatchOptionsMenu: '<c-p>'
|
||||||
nextTab: ']'
|
nextTab: ']'
|
||||||
prevTab: '['
|
prevTab: '['
|
||||||
|
nextScreenMode: '+'
|
||||||
|
prevScreenMode: '_'
|
||||||
status:
|
status:
|
||||||
checkForUpdate: 'u'
|
checkForUpdate: 'u'
|
||||||
recentRepos: '<enter>'
|
recentRepos: '<enter>'
|
||||||
|
180
pkg/gui/gui.go
180
pkg/gui/gui.go
@ -33,6 +33,12 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SCREEN_NORMAL int = iota
|
||||||
|
SCREEN_HALF
|
||||||
|
SCREEN_FULL
|
||||||
|
)
|
||||||
|
|
||||||
const StartupPopupVersion = 1
|
const StartupPopupVersion = 1
|
||||||
|
|
||||||
// OverlappingEdges determines if panel edges overlap
|
// OverlappingEdges determines if panel edges overlap
|
||||||
@ -202,6 +208,8 @@ type guiState struct {
|
|||||||
IsRefreshingFiles bool
|
IsRefreshingFiles bool
|
||||||
RefreshingFilesMutex sync.Mutex
|
RefreshingFilesMutex sync.Mutex
|
||||||
Searching searchingState
|
Searching searchingState
|
||||||
|
ScreenMode int
|
||||||
|
SideView *gocui.View
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now the split view will always be on
|
// for now the split view will always be on
|
||||||
@ -236,6 +244,8 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
|
|||||||
},
|
},
|
||||||
Status: &statusPanelState{},
|
Status: &statusPanelState{},
|
||||||
},
|
},
|
||||||
|
ScreenMode: SCREEN_NORMAL,
|
||||||
|
SideView: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
gui := &Gui{
|
gui := &Gui{
|
||||||
@ -257,6 +267,18 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
|
|||||||
return gui, nil
|
return gui, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
gui.State.ScreenMode = utils.NextIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
gui.State.ScreenMode = utils.PrevIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (gui *Gui) scrollUpView(viewName string) error {
|
func (gui *Gui) scrollUpView(viewName string) error {
|
||||||
mainView, _ := gui.g.View(viewName)
|
mainView, _ := gui.g.View(viewName)
|
||||||
ox, oy := mainView.Origin()
|
ox, oy := mainView.Origin()
|
||||||
@ -379,6 +401,75 @@ func (gui *Gui) onFocus(v *gocui.View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) getViewHeights() map[string]int {
|
||||||
|
currView := gui.g.CurrentView()
|
||||||
|
currentCyclebleView := gui.State.PreviousView
|
||||||
|
if currView != nil {
|
||||||
|
viewName := currView.Name()
|
||||||
|
usePreviousView := true
|
||||||
|
for _, view := range cyclableViews {
|
||||||
|
if view == viewName {
|
||||||
|
currentCyclebleView = viewName
|
||||||
|
usePreviousView = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if usePreviousView {
|
||||||
|
currentCyclebleView = gui.State.PreviousView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unfortunate result of the fact that these are separate views, have to map explicitly
|
||||||
|
if currentCyclebleView == "commitFiles" {
|
||||||
|
currentCyclebleView = "commits"
|
||||||
|
}
|
||||||
|
|
||||||
|
_, height := gui.g.Size()
|
||||||
|
|
||||||
|
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF {
|
||||||
|
vHeights := map[string]int{
|
||||||
|
"status": 0,
|
||||||
|
"files": 0,
|
||||||
|
"branches": 0,
|
||||||
|
"commits": 0,
|
||||||
|
"stash": 0,
|
||||||
|
"options": 0,
|
||||||
|
}
|
||||||
|
vHeights[currentCyclebleView] = height - 1
|
||||||
|
return vHeights
|
||||||
|
}
|
||||||
|
|
||||||
|
usableSpace := height - 7
|
||||||
|
extraSpace := usableSpace - (usableSpace/3)*3
|
||||||
|
|
||||||
|
if height >= 28 {
|
||||||
|
return map[string]int{
|
||||||
|
"status": 3,
|
||||||
|
"files": (usableSpace / 3) + extraSpace,
|
||||||
|
"branches": usableSpace / 3,
|
||||||
|
"commits": usableSpace / 3,
|
||||||
|
"stash": 3,
|
||||||
|
"options": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultHeight := 3
|
||||||
|
if height < 21 {
|
||||||
|
defaultHeight = 1
|
||||||
|
}
|
||||||
|
vHeights := map[string]int{
|
||||||
|
"status": defaultHeight,
|
||||||
|
"files": defaultHeight,
|
||||||
|
"branches": defaultHeight,
|
||||||
|
"commits": defaultHeight,
|
||||||
|
"stash": defaultHeight,
|
||||||
|
"options": defaultHeight,
|
||||||
|
}
|
||||||
|
vHeights[currentCyclebleView] = height - defaultHeight*4 - 1
|
||||||
|
|
||||||
|
return vHeights
|
||||||
|
}
|
||||||
|
|
||||||
// layout is called for every screen re-render e.g. when the screen is resized
|
// layout is called for every screen re-render e.g. when the screen is resized
|
||||||
func (gui *Gui) layout(g *gocui.Gui) error {
|
func (gui *Gui) layout(g *gocui.Gui) error {
|
||||||
g.Highlight = true
|
g.Highlight = true
|
||||||
@ -405,50 +496,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
currView := gui.g.CurrentView()
|
vHeights := gui.getViewHeights()
|
||||||
currentCyclebleView := gui.State.PreviousView
|
|
||||||
if currView != nil {
|
|
||||||
viewName := currView.Name()
|
|
||||||
usePreviouseView := true
|
|
||||||
for _, view := range cyclableViews {
|
|
||||||
if view == viewName {
|
|
||||||
currentCyclebleView = viewName
|
|
||||||
usePreviouseView = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if usePreviouseView {
|
|
||||||
currentCyclebleView = gui.State.PreviousView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usableSpace := height - 7
|
|
||||||
extraSpace := usableSpace - (usableSpace/3)*3
|
|
||||||
|
|
||||||
vHeights := map[string]int{
|
|
||||||
"status": 3,
|
|
||||||
"files": (usableSpace / 3) + extraSpace,
|
|
||||||
"branches": usableSpace / 3,
|
|
||||||
"commits": usableSpace / 3,
|
|
||||||
"stash": 3,
|
|
||||||
"options": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
if height < 28 {
|
|
||||||
defaultHeight := 3
|
|
||||||
if height < 21 {
|
|
||||||
defaultHeight = 1
|
|
||||||
}
|
|
||||||
vHeights = map[string]int{
|
|
||||||
"status": defaultHeight,
|
|
||||||
"files": defaultHeight,
|
|
||||||
"branches": defaultHeight,
|
|
||||||
"commits": defaultHeight,
|
|
||||||
"stash": defaultHeight,
|
|
||||||
"options": defaultHeight,
|
|
||||||
}
|
|
||||||
vHeights[currentCyclebleView] = height - defaultHeight*4 - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
|
optionsVersionBoundary := width - max(len(utils.Decolorise(information)), 1)
|
||||||
|
|
||||||
@ -458,29 +506,45 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
appStatusOptionsBoundary = len(appStatus) + 2
|
appStatusOptionsBoundary = len(appStatus) + 2
|
||||||
}
|
}
|
||||||
|
|
||||||
panelSpacing := 1
|
|
||||||
if OverlappingEdges {
|
|
||||||
panelSpacing = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = g.SetViewOnBottom("limit")
|
_, _ = g.SetViewOnBottom("limit")
|
||||||
g.DeleteView("limit")
|
g.DeleteView("limit")
|
||||||
|
|
||||||
textColor := theme.GocuiDefaultTextColor
|
textColor := theme.GocuiDefaultTextColor
|
||||||
leftSideWidth := width / 3
|
var leftSideWidth int
|
||||||
|
switch gui.State.ScreenMode {
|
||||||
|
case SCREEN_NORMAL:
|
||||||
|
leftSideWidth = width / 3
|
||||||
|
case SCREEN_HALF:
|
||||||
|
leftSideWidth = width / 2
|
||||||
|
case SCREEN_FULL:
|
||||||
|
currentView := gui.g.CurrentView()
|
||||||
|
if currentView != nil && currentView.Name() == "main" {
|
||||||
|
leftSideWidth = 0
|
||||||
|
} else {
|
||||||
|
leftSideWidth = width - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
panelSplitX := width - 1
|
panelSplitX := width - 1
|
||||||
|
mainPanelLeft := leftSideWidth + 1
|
||||||
mainPanelRight := width - 1
|
mainPanelRight := width - 1
|
||||||
secondaryPanelLeft := width - 1
|
secondaryPanelLeft := width - 1
|
||||||
secondaryPanelTop := 0
|
secondaryPanelTop := 0
|
||||||
mainPanelBottom := height - 2
|
mainPanelBottom := height - 2
|
||||||
if gui.State.SplitMainPanel {
|
if gui.State.SplitMainPanel {
|
||||||
if width < 220 {
|
if gui.State.ScreenMode == SCREEN_FULL {
|
||||||
|
mainPanelLeft = 0
|
||||||
|
panelSplitX = width/2 - 4
|
||||||
|
mainPanelRight = panelSplitX
|
||||||
|
secondaryPanelLeft = panelSplitX + 1
|
||||||
|
} else if width < 220 {
|
||||||
mainPanelBottom = height/2 - 1
|
mainPanelBottom = height/2 - 1
|
||||||
secondaryPanelTop = mainPanelBottom + 1
|
secondaryPanelTop = mainPanelBottom + 1
|
||||||
secondaryPanelLeft = leftSideWidth + 1
|
secondaryPanelLeft = leftSideWidth + 1
|
||||||
} else {
|
} else {
|
||||||
units := 5
|
units := 5
|
||||||
leftSideWidth = width / units
|
leftSideWidth = width / units
|
||||||
|
mainPanelLeft = leftSideWidth + 1
|
||||||
panelSplitX = (1 + ((units - 1) / 2)) * width / units
|
panelSplitX = (1 + ((units - 1) / 2)) * width / units
|
||||||
mainPanelRight = panelSplitX
|
mainPanelRight = panelSplitX
|
||||||
secondaryPanelLeft = panelSplitX + 1
|
secondaryPanelLeft = panelSplitX + 1
|
||||||
@ -510,7 +574,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := g.SetView(main, leftSideWidth+panelSpacing, 0, mainPanelRight, mainPanelBottom, gocui.LEFT)
|
v, err := g.SetView(main, mainPanelLeft, 0, mainPanelRight, mainPanelBottom, gocui.LEFT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
@ -519,16 +583,15 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
v.Wrap = true
|
v.Wrap = true
|
||||||
v.FgColor = textColor
|
v.FgColor = textColor
|
||||||
v.IgnoreCarriageReturns = true
|
v.IgnoreCarriageReturns = true
|
||||||
v.SetOnSelectItem(gui.onSelectItemWrapper(func(selectedLine int) error {
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hiddenViewOffset := 0
|
hiddenViewOffset := 9999
|
||||||
|
|
||||||
|
hiddenSecondaryPanelOffset := 0
|
||||||
if !gui.State.SplitMainPanel {
|
if !gui.State.SplitMainPanel {
|
||||||
hiddenViewOffset = 9999
|
hiddenSecondaryPanelOffset = hiddenViewOffset
|
||||||
}
|
}
|
||||||
secondaryView, err := g.SetView(secondary, secondaryPanelLeft+hiddenViewOffset, hiddenViewOffset+secondaryPanelTop, width-1+hiddenViewOffset, height-2+hiddenViewOffset, gocui.LEFT)
|
secondaryView, err := g.SetView(secondary, secondaryPanelLeft+hiddenSecondaryPanelOffset, hiddenSecondaryPanelOffset+secondaryPanelTop, width-1+hiddenSecondaryPanelOffset, height-2+hiddenSecondaryPanelOffset, gocui.LEFT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() != "unknown view" {
|
if err.Error() != "unknown view" {
|
||||||
return err
|
return err
|
||||||
@ -577,6 +640,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
|||||||
v.Title = gui.Tr.SLocalize("CommitFiles")
|
v.Title = gui.Tr.SLocalize("CommitFiles")
|
||||||
v.FgColor = textColor
|
v.FgColor = textColor
|
||||||
v.SetOnSelectItem(gui.onSelectItemWrapper(gui.onCommitFilesPanelSearchSelect))
|
v.SetOnSelectItem(gui.onSelectItemWrapper(gui.onCommitFilesPanelSearchSelect))
|
||||||
|
v.ContainsList = true
|
||||||
}
|
}
|
||||||
|
|
||||||
commitsView, err := g.SetViewBeneath("commits", "branches", vHeights["commits"])
|
commitsView, err := g.SetViewBeneath("commits", "branches", vHeights["commits"])
|
||||||
|
@ -317,6 +317,20 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||||||
Handler: gui.handleEditConfig,
|
Handler: gui.handleEditConfig,
|
||||||
Description: gui.Tr.SLocalize("EditConfig"),
|
Description: gui.Tr.SLocalize("EditConfig"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ViewName: "",
|
||||||
|
Key: gui.getKey("universal.nextScreenMode"),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.nextScreenMode,
|
||||||
|
Description: gui.Tr.SLocalize("nextScreenMode"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ViewName: "",
|
||||||
|
Key: gui.getKey("universal.prevScreenMode"),
|
||||||
|
Modifier: gocui.ModNone,
|
||||||
|
Handler: gui.prevScreenMode,
|
||||||
|
Description: gui.Tr.SLocalize("prevScreenMode"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ViewName: "status",
|
ViewName: "status",
|
||||||
Key: gui.getKey("universal.openFile"),
|
Key: gui.getKey("universal.openFile"),
|
||||||
|
@ -945,6 +945,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
|
|||||||
}, &i18n.Message{
|
}, &i18n.Message{
|
||||||
ID: "viewResetToUpstreamOptions",
|
ID: "viewResetToUpstreamOptions",
|
||||||
Other: "view upstream reset options",
|
Other: "view upstream reset options",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "nextScreenMode",
|
||||||
|
Other: "next screen mode (normal/half/fullscreen)",
|
||||||
|
}, &i18n.Message{
|
||||||
|
ID: "prevScreenMode",
|
||||||
|
Other: "prev screen mode",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -322,3 +322,29 @@ func ModuloWithWrap(n, max int) int {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NextIntInCycle returns the next int in a slice, returning to the first index if we've reached the end
|
||||||
|
func NextIntInCycle(sl []int, current int) int {
|
||||||
|
for i, val := range sl {
|
||||||
|
if val == current {
|
||||||
|
if i == len(sl)-1 {
|
||||||
|
return sl[0]
|
||||||
|
}
|
||||||
|
return sl[i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sl[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrevIntInCycle returns the prev int in a slice, returning to the first index if we've reached the end
|
||||||
|
func PrevIntInCycle(sl []int, current int) int {
|
||||||
|
for i, val := range sl {
|
||||||
|
if val == current {
|
||||||
|
if i > 0 {
|
||||||
|
return sl[i-1]
|
||||||
|
}
|
||||||
|
return sl[len(sl)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sl[len(sl)-1]
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user