1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-11 13:53:25 +02:00

move box layout stuff into its own package

This commit is contained in:
Jesse Duffield 2020-08-15 08:28:02 +10:00
parent 8430b04492
commit 1e12a60b34
3 changed files with 130 additions and 129 deletions

View File

@ -1,19 +1,20 @@
package gui
import (
"github.com/jesseduffield/lazygit/pkg/gui/boxlayout"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) mainSectionChildren() []*box {
func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
currentViewName := gui.currentViewName()
// 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 !gui.State.SplitMainPanel || (gui.State.ScreenMode == SCREEN_FULL && currentViewName == "main") {
return []*box{
return []*boxlayout.Box{
{
viewName: "main",
weight: 1,
ViewName: "main",
Weight: 1,
},
}
}
@ -25,14 +26,14 @@ func (gui *Gui) mainSectionChildren() []*box {
main, secondary = secondary, main
}
return []*box{
return []*boxlayout.Box{
{
viewName: main,
weight: 1,
ViewName: main,
Weight: 1,
},
{
viewName: secondary,
weight: 1,
ViewName: secondary,
Weight: 1,
},
}
}
@ -65,41 +66,41 @@ func (gui *Gui) getMidSectionWeights() (int, int) {
return sideSectionWeight, mainSectionWeight
}
func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*box {
func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*boxlayout.Box {
if gui.State.Searching.isSearching {
return []*box{
return []*boxlayout.Box{
{
viewName: "searchPrefix",
size: len(SEARCH_PREFIX),
ViewName: "searchPrefix",
Size: len(SEARCH_PREFIX),
},
{
viewName: "search",
weight: 1,
ViewName: "search",
Weight: 1,
},
}
}
result := []*box{}
result := []*boxlayout.Box{}
if len(appStatus) > 0 {
result = append(result,
&box{
viewName: "appStatus",
size: len(appStatus) + len(INFO_SECTION_PADDING),
&boxlayout.Box{
ViewName: "appStatus",
Size: len(appStatus) + len(INFO_SECTION_PADDING),
},
)
}
result = append(result,
[]*box{
[]*boxlayout.Box{
{
viewName: "options",
weight: 1,
ViewName: "options",
Weight: 1,
},
{
viewName: "information",
ViewName: "information",
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length
size: len(INFO_SECTION_PADDING) + len(utils.Decolorise(informationStr)),
Size: len(INFO_SECTION_PADDING) + len(utils.Decolorise(informationStr)),
},
}...,
)
@ -107,82 +108,82 @@ func (gui *Gui) infoSectionChildren(informationStr string, appStatus string) []*
return result
}
func (gui *Gui) getViewDimensions(informationStr string, appStatus string) map[string]dimensions {
func (gui *Gui) getViewDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
width, height := gui.g.Size()
sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
sidePanelsDirection := COLUMN
sidePanelsDirection := boxlayout.COLUMN
portraitMode := width <= 84 && height > 45
if portraitMode {
sidePanelsDirection = ROW
sidePanelsDirection = boxlayout.ROW
}
root := &box{
direction: ROW,
children: []*box{
root := &boxlayout.Box{
Direction: boxlayout.ROW,
Children: []*boxlayout.Box{
{
direction: sidePanelsDirection,
weight: 1,
children: []*box{
Direction: sidePanelsDirection,
Weight: 1,
Children: []*boxlayout.Box{
{
direction: ROW,
weight: sideSectionWeight,
conditionalChildren: gui.sidePanelChildren,
Direction: boxlayout.ROW,
Weight: sideSectionWeight,
ConditionalChildren: gui.sidePanelChildren,
},
{
conditionalDirection: func(width int, height int) int {
ConditionalDirection: func(width int, height int) int {
mainPanelSplitMode := gui.Config.GetUserConfig().GetString("gui.mainPanelSplitMode")
switch mainPanelSplitMode {
case "vertical":
return ROW
return boxlayout.ROW
case "horizontal":
return COLUMN
return boxlayout.COLUMN
default:
if width < 160 && height > 30 { // 2 80 character width panels
return ROW
return boxlayout.ROW
} else {
return COLUMN
return boxlayout.COLUMN
}
}
},
direction: COLUMN,
weight: mainSectionWeight,
children: gui.mainSectionChildren(),
Direction: boxlayout.COLUMN,
Weight: mainSectionWeight,
Children: gui.mainSectionChildren(),
},
},
},
{
direction: COLUMN,
size: 1,
children: gui.infoSectionChildren(informationStr, appStatus),
Direction: boxlayout.COLUMN,
Size: 1,
Children: gui.infoSectionChildren(informationStr, appStatus),
},
},
}
return gui.arrangeViews(root, 0, 0, width, height)
return boxlayout.ArrangeViews(root, 0, 0, width, height)
}
func (gui *Gui) sidePanelChildren(width int, height int) []*box {
func (gui *Gui) sidePanelChildren(width int, height int) []*boxlayout.Box {
currentCyclableViewName := gui.currentCyclableViewName()
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF {
fullHeightBox := func(viewName string) *box {
fullHeightBox := func(viewName string) *boxlayout.Box {
if viewName == currentCyclableViewName {
return &box{
viewName: viewName,
weight: 1,
return &boxlayout.Box{
ViewName: viewName,
Weight: 1,
}
} else {
return &box{
viewName: viewName,
size: 0,
return &boxlayout.Box{
ViewName: viewName,
Size: 0,
}
}
}
return []*box{
return []*boxlayout.Box{
fullHeightBox("status"),
fullHeightBox("files"),
fullHeightBox("branches"),
@ -191,26 +192,26 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*box {
}
} else if height >= 28 {
accordianMode := gui.Config.GetUserConfig().GetBool("gui.expandFocusedSidePanel")
accordianBox := func(defaultBox *box) *box {
if accordianMode && defaultBox.viewName == currentCyclableViewName {
return &box{
viewName: defaultBox.viewName,
weight: 2,
accordianBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
if accordianMode && defaultBox.ViewName == currentCyclableViewName {
return &boxlayout.Box{
ViewName: defaultBox.ViewName,
Weight: 2,
}
}
return defaultBox
}
return []*box{
return []*boxlayout.Box{
{
viewName: "status",
size: 3,
ViewName: "status",
Size: 3,
},
accordianBox(&box{viewName: "files", weight: 1}),
accordianBox(&box{viewName: "branches", weight: 1}),
accordianBox(&box{viewName: "commits", weight: 1}),
accordianBox(&box{viewName: "stash", size: 3}),
accordianBox(&boxlayout.Box{ViewName: "files", Weight: 1}),
accordianBox(&boxlayout.Box{ViewName: "branches", Weight: 1}),
accordianBox(&boxlayout.Box{ViewName: "commits", Weight: 1}),
accordianBox(&boxlayout.Box{ViewName: "stash", Size: 3}),
}
} else {
squashedHeight := 1
@ -218,21 +219,21 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*box {
squashedHeight = 3
}
squashedSidePanelBox := func(viewName string) *box {
squashedSidePanelBox := func(viewName string) *boxlayout.Box {
if viewName == currentCyclableViewName {
return &box{
viewName: viewName,
weight: 1,
return &boxlayout.Box{
ViewName: viewName,
Weight: 1,
}
} else {
return &box{
viewName: viewName,
size: squashedHeight,
return &boxlayout.Box{
ViewName: viewName,
Size: squashedHeight,
}
}
}
return []*box{
return []*boxlayout.Box{
squashedSidePanelBox("status"),
squashedSidePanelBox("files"),
squashedSidePanelBox("branches"),

View File

@ -1,12 +1,12 @@
package gui
package boxlayout
import "math"
type dimensions struct {
x0 int
x1 int
y0 int
y1 int
type Dimensions struct {
X0 int
X1 int
Y0 int
Y1 int
}
const (
@ -24,56 +24,56 @@ const (
// boxes, one with weight 1 and the other with weight 2, the first one gets 33%
// of the available space and the second one gets the remaining 66%
type box struct {
// direction decides how the children boxes are laid out. ROW means the children will each form a row i.e. that they will be stacked on top of eachother.
direction int // ROW or COLUMN
type Box struct {
// Direction decides how the children boxes are laid out. ROW means the children will each form a row i.e. that they will be stacked on top of eachother.
Direction int // ROW or COLUMN
// function which takes the width and height assigned to the box and decides which orientation it will have
conditionalDirection func(width int, height int) int
ConditionalDirection func(width int, height int) int
children []*box
Children []*Box
// function which takes the width and height assigned to the box and decides the layout of the children.
conditionalChildren func(width int, height int) []*box
ConditionalChildren func(width int, height int) []*Box
// viewName refers to the name of the view this box represents, if there is one
viewName string
// ViewName refers to the name of the view this box represents, if there is one
ViewName string
// static size. If parent box's direction is ROW this refers to height, otherwise width
size int
// static Size. If parent box's direction is ROW this refers to height, otherwise width
Size int
// dynamic size. Once all statically sized children have been considered, weight decides how much of the remaining space will be taken up by the box
// TODO: consider making there be one int and a type enum so we can't have size and weight simultaneously defined
weight int
// dynamic size. Once all statically sized children have been considered, Weight decides how much of the remaining space will be taken up by the box
// TODO: consider making there be one int and a type enum so we can't have size and Weight simultaneously defined
Weight int
}
func (b *box) isStatic() bool {
return b.size > 0
func (b *Box) isStatic() bool {
return b.Size > 0
}
func (b *box) getDirection(width int, height int) int {
if b.conditionalDirection != nil {
return b.conditionalDirection(width, height)
func (b *Box) getDirection(width int, height int) int {
if b.ConditionalDirection != nil {
return b.ConditionalDirection(width, height)
}
return b.direction
return b.Direction
}
func (b *box) getChildren(width int, height int) []*box {
if b.conditionalChildren != nil {
return b.conditionalChildren(width, height)
func (b *Box) getChildren(width int, height int) []*Box {
if b.ConditionalChildren != nil {
return b.ConditionalChildren(width, height)
}
return b.children
return b.Children
}
func (gui *Gui) arrangeViews(root *box, x0, y0, width, height int) map[string]dimensions {
func ArrangeViews(root *Box, x0, y0, width, height int) map[string]Dimensions {
children := root.getChildren(width, height)
if len(children) == 0 {
// leaf node
if root.viewName != "" {
dimensionsForView := dimensions{x0: x0, y0: y0, x1: x0 + width - 1, y1: y0 + height - 1}
return map[string]dimensions{root.viewName: dimensionsForView}
if root.ViewName != "" {
dimensionsForView := Dimensions{X0: x0, Y0: y0, X1: x0 + width - 1, Y1: y0 + height - 1}
return map[string]Dimensions{root.ViewName: dimensionsForView}
}
return map[string]dimensions{}
return map[string]Dimensions{}
}
direction := root.getDirection(width, height)
@ -90,8 +90,8 @@ func (gui *Gui) arrangeViews(root *box, x0, y0, width, height int) map[string]di
totalWeight := 0
for _, child := range children {
// assuming either size or weight are non-zero
reservedSize += child.size
totalWeight += child.weight
reservedSize += child.Size
totalWeight += child.Weight
}
remainingSize := availableSize - reservedSize
@ -106,37 +106,37 @@ func (gui *Gui) arrangeViews(root *box, x0, y0, width, height int) map[string]di
extraSize = remainingSize % totalWeight
}
result := map[string]dimensions{}
result := map[string]Dimensions{}
offset := 0
for _, child := range children {
var boxSize int
if child.isStatic() {
boxSize = child.size
boxSize = child.Size
} else {
// TODO: consider more evenly distributing the remainder
boxSize = unitSize * child.weight
boxExtraSize := int(math.Min(float64(extraSize), float64(child.weight)))
boxSize = unitSize * child.Weight
boxExtraSize := int(math.Min(float64(extraSize), float64(child.Weight)))
boxSize += boxExtraSize
extraSize -= boxExtraSize
}
var resultForChild map[string]dimensions
var resultForChild map[string]Dimensions
if direction == COLUMN {
resultForChild = gui.arrangeViews(child, x0+offset, y0, boxSize, height)
resultForChild = ArrangeViews(child, x0+offset, y0, boxSize, height)
} else {
resultForChild = gui.arrangeViews(child, x0, y0+offset, width, boxSize)
resultForChild = ArrangeViews(child, x0, y0+offset, width, boxSize)
}
result = gui.mergeDimensionMaps(result, resultForChild)
result = mergeDimensionMaps(result, resultForChild)
offset += boxSize
}
return result
}
func (gui *Gui) mergeDimensionMaps(a map[string]dimensions, b map[string]dimensions) map[string]dimensions {
result := map[string]dimensions{}
for _, dimensionMap := range []map[string]dimensions{a, b} {
func mergeDimensionMaps(a map[string]Dimensions, b map[string]Dimensions) map[string]Dimensions {
result := map[string]Dimensions{}
for _, dimensionMap := range []map[string]Dimensions{a, b} {
for k, v := range dimensionMap {
result[k] = v
}

View File

@ -123,7 +123,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
prevMainView, err := gui.g.View("main")
if err == nil {
_, prevMainHeight := prevMainView.Size()
newMainHeight := viewDimensions["main"].y1 - viewDimensions["main"].y0 - 1
newMainHeight := viewDimensions["main"].Y1 - viewDimensions["main"].Y0 - 1
heightDiff := newMainHeight - prevMainHeight
if heightDiff > 0 {
if manager, ok := gui.viewBufferManagerMap["main"]; ok {
@ -143,10 +143,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
return g.SetView(
viewName,
dimensionsObj.x0-frameOffset,
dimensionsObj.y0-frameOffset,
dimensionsObj.x1+frameOffset,
dimensionsObj.y1+frameOffset,
dimensionsObj.X0-frameOffset,
dimensionsObj.Y0-frameOffset,
dimensionsObj.X1+frameOffset,
dimensionsObj.Y1+frameOffset,
0,
)
}