1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-13 11:50:28 +02:00
lazygit/pkg/gui/controllers/helpers/window_arrangement_helper.go
Jesse Duffield d772c9f1d4 Use sentence case everywhere
We have not been good at consistent casing so far. Now we use 'Sentence case' everywhere. EVERYWHERE.

Also Removing 'Lc' prefix from i18n field names: the 'Lc' stood for lowercase but now that everything
is in 'Sentence case' there's no need for the distinction.

I've got a couple lower case things I've kept: namely, things that show up in parentheses.
2023-05-25 23:52:19 +10:00

355 lines
9.2 KiB
Go

package helpers
import (
"github.com/jesseduffield/lazycore/pkg/boxlayout"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mattn/go-runewidth"
)
// In this file we use the boxlayout package, along with knowledge about the app's state,
// to arrange the windows (i.e. panels) on the screen.
type WindowArrangementHelper struct {
c *HelperCommon
windowHelper *WindowHelper
modeHelper *ModeHelper
appStatusHelper *AppStatusHelper
}
func NewWindowArrangementHelper(
c *HelperCommon,
windowHelper *WindowHelper,
modeHelper *ModeHelper,
appStatusHelper *AppStatusHelper,
) *WindowArrangementHelper {
return &WindowArrangementHelper{
c: c,
windowHelper: windowHelper,
modeHelper: modeHelper,
appStatusHelper: appStatusHelper,
}
}
const INFO_SECTION_PADDING = " "
func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
width, height := self.c.GocuiGui().Size()
sideSectionWeight, mainSectionWeight := self.getMidSectionWeights()
sidePanelsDirection := boxlayout.COLUMN
portraitMode := width <= 84 && height > 45
if portraitMode {
sidePanelsDirection = boxlayout.ROW
}
mainPanelsDirection := boxlayout.ROW
if self.splitMainPanelSideBySide() {
mainPanelsDirection = boxlayout.COLUMN
}
extrasWindowSize := self.getExtrasWindowSize(height)
self.c.Modes().Filtering.Active()
showInfoSection := self.c.UserConfig.Gui.ShowBottomLine ||
self.c.State().GetRepoState().IsSearching() ||
self.modeHelper.IsAnyModeActive() ||
self.appStatusHelper.HasStatus()
infoSectionSize := 0
if showInfoSection {
infoSectionSize = 1
}
root := &boxlayout.Box{
Direction: boxlayout.ROW,
Children: []*boxlayout.Box{
{
Direction: sidePanelsDirection,
Weight: 1,
Children: []*boxlayout.Box{
{
Direction: boxlayout.ROW,
Weight: sideSectionWeight,
ConditionalChildren: self.sidePanelChildren,
},
{
Direction: boxlayout.ROW,
Weight: mainSectionWeight,
Children: []*boxlayout.Box{
{
Direction: mainPanelsDirection,
Children: self.mainSectionChildren(),
Weight: 1,
},
{
Window: "extras",
Size: extrasWindowSize,
},
},
},
},
},
{
Direction: boxlayout.COLUMN,
Size: infoSectionSize,
Children: self.infoSectionChildren(informationStr, appStatus),
},
},
}
layerOneWindows := boxlayout.ArrangeWindows(root, 0, 0, width, height)
limitWindows := boxlayout.ArrangeWindows(&boxlayout.Box{Window: "limit"}, 0, 0, width, height)
return MergeMaps(layerOneWindows, limitWindows)
}
func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
result := map[K]V{}
for _, currMap := range maps {
for key, value := range currMap {
result[key] = value
}
}
return result
}
func (self *WindowArrangementHelper) mainSectionChildren() []*boxlayout.Box {
currentWindow := self.windowHelper.CurrentWindow()
// if we're not in split mode we can just show the one main panel. Likewise if
// the main panel is focused and we're in full-screen mode
if !self.c.State().GetRepoState().GetSplitMainPanel() || (self.c.State().GetRepoState().GetScreenMode() == types.SCREEN_FULL && currentWindow == "main") {
return []*boxlayout.Box{
{
Window: "main",
Weight: 1,
},
}
}
return []*boxlayout.Box{
{
Window: "main",
Weight: 1,
},
{
Window: "secondary",
Weight: 1,
},
}
}
func (self *WindowArrangementHelper) getMidSectionWeights() (int, int) {
currentWindow := self.windowHelper.CurrentWindow()
// we originally specified this as a ratio i.e. .20 would correspond to a weight of 1 against 4
sidePanelWidthRatio := self.c.UserConfig.Gui.SidePanelWidth
// we could make this better by creating ratios like 2:3 rather than always 1:something
mainSectionWeight := int(1/sidePanelWidthRatio) - 1
sideSectionWeight := 1
if self.splitMainPanelSideBySide() {
mainSectionWeight = 5 // need to shrink side panel to make way for main panels if side-by-side
}
screenMode := self.c.State().GetRepoState().GetScreenMode()
if currentWindow == "main" {
if screenMode == types.SCREEN_HALF || screenMode == types.SCREEN_FULL {
sideSectionWeight = 0
}
} else {
if screenMode == types.SCREEN_HALF {
mainSectionWeight = 1
} else if screenMode == types.SCREEN_FULL {
mainSectionWeight = 0
}
}
return sideSectionWeight, mainSectionWeight
}
func (self *WindowArrangementHelper) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box {
if self.c.State().GetRepoState().IsSearching() {
return []*boxlayout.Box{
{
Window: "searchPrefix",
Size: runewidth.StringWidth(self.c.Tr.SearchPrefix),
},
{
Window: "search",
Weight: 1,
},
}
}
appStatusBox := &boxlayout.Box{Window: "appStatus"}
optionsBox := &boxlayout.Box{Window: "options"}
if !self.c.UserConfig.Gui.ShowBottomLine {
optionsBox.Weight = 0
appStatusBox.Weight = 1
} else {
optionsBox.Weight = 1
appStatusBox.Size = runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(appStatus)
}
result := []*boxlayout.Box{appStatusBox, optionsBox}
if self.c.UserConfig.Gui.ShowBottomLine || self.modeHelper.IsAnyModeActive() {
result = append(result, &boxlayout.Box{
Window: "information",
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
Size: runewidth.StringWidth(INFO_SECTION_PADDING) + runewidth.StringWidth(utils.Decolorise(informationStr)),
})
}
return result
}
func (self *WindowArrangementHelper) splitMainPanelSideBySide() bool {
if !self.c.State().GetRepoState().GetSplitMainPanel() {
return false
}
mainPanelSplitMode := self.c.UserConfig.Gui.MainPanelSplitMode
width, height := self.c.GocuiGui().Size()
switch mainPanelSplitMode {
case "vertical":
return false
case "horizontal":
return true
default:
if width < 200 && height > 30 { // 2 80 character width panels + 40 width for side panel
return false
} else {
return true
}
}
}
func (self *WindowArrangementHelper) getExtrasWindowSize(screenHeight int) int {
if !self.c.State().GetShowExtrasWindow() {
return 0
}
var baseSize int
if self.c.CurrentStaticContext().GetKey() == context.COMMAND_LOG_CONTEXT_KEY {
baseSize = 1000 // my way of saying 'fill the available space'
} else if screenHeight < 40 {
baseSize = 1
} else {
baseSize = self.c.UserConfig.Gui.CommandLogSize
}
frameSize := 2
return baseSize + frameSize
}
// The stash window by default only contains one line so that it's not hogging
// too much space, but if you access it it should take up some space. This is
// the default behaviour when accordion mode is NOT in effect. If it is in effect
// then when it's accessed it will have weight 2, not 1.
func (self *WindowArrangementHelper) getDefaultStashWindowBox() *boxlayout.Box {
stashWindowAccessed := false
self.c.Context().ForEach(func(context types.Context) {
if context.GetWindowName() == "stash" {
stashWindowAccessed = true
}
})
box := &boxlayout.Box{Window: "stash"}
// if the stash window is anywhere in our stack we should enlargen it
if stashWindowAccessed {
box.Weight = 1
} else {
box.Size = 3
}
return box
}
func (self *WindowArrangementHelper) sidePanelChildren(width int, height int) []*boxlayout.Box {
currentWindow := self.c.CurrentSideContext().GetWindowName()
screenMode := self.c.State().GetRepoState().GetScreenMode()
if screenMode == types.SCREEN_FULL || screenMode == types.SCREEN_HALF {
fullHeightBox := func(window string) *boxlayout.Box {
if window == currentWindow {
return &boxlayout.Box{
Window: window,
Weight: 1,
}
} else {
return &boxlayout.Box{
Window: window,
Size: 0,
}
}
}
return []*boxlayout.Box{
fullHeightBox("status"),
fullHeightBox("files"),
fullHeightBox("branches"),
fullHeightBox("commits"),
fullHeightBox("stash"),
}
} else if height >= 28 {
accordionMode := self.c.UserConfig.Gui.ExpandFocusedSidePanel
accordionBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
if accordionMode && defaultBox.Window == currentWindow {
return &boxlayout.Box{
Window: defaultBox.Window,
Weight: 2,
}
}
return defaultBox
}
return []*boxlayout.Box{
{
Window: "status",
Size: 3,
},
accordionBox(&boxlayout.Box{Window: "files", Weight: 1}),
accordionBox(&boxlayout.Box{Window: "branches", Weight: 1}),
accordionBox(&boxlayout.Box{Window: "commits", Weight: 1}),
accordionBox(self.getDefaultStashWindowBox()),
}
} else {
squashedHeight := 1
if height >= 21 {
squashedHeight = 3
}
squashedSidePanelBox := func(window string) *boxlayout.Box {
if window == currentWindow {
return &boxlayout.Box{
Window: window,
Weight: 1,
}
} else {
return &boxlayout.Box{
Window: window,
Size: squashedHeight,
}
}
}
return []*boxlayout.Box{
squashedSidePanelBox("status"),
squashedSidePanelBox("files"),
squashedSidePanelBox("branches"),
squashedSidePanelBox("commits"),
squashedSidePanelBox("stash"),
}
}
}