1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-08 23:56:15 +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 package gui
import ( import (
"github.com/jesseduffield/lazygit/pkg/gui/boxlayout"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
func (gui *Gui) mainSectionChildren() []*box { func (gui *Gui) mainSectionChildren() []*boxlayout.Box {
currentViewName := gui.currentViewName() currentViewName := gui.currentViewName()
// if we're not in split mode we can just show the one main panel. Likewise if // 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 // the main panel is focused and we're in full-screen mode
if !gui.State.SplitMainPanel || (gui.State.ScreenMode == SCREEN_FULL && currentViewName == "main") { if !gui.State.SplitMainPanel || (gui.State.ScreenMode == SCREEN_FULL && currentViewName == "main") {
return []*box{ return []*boxlayout.Box{
{ {
viewName: "main", ViewName: "main",
weight: 1, Weight: 1,
}, },
} }
} }
@ -25,14 +26,14 @@ func (gui *Gui) mainSectionChildren() []*box {
main, secondary = secondary, main main, secondary = secondary, main
} }
return []*box{ return []*boxlayout.Box{
{ {
viewName: main, ViewName: main,
weight: 1, Weight: 1,
}, },
{ {
viewName: secondary, ViewName: secondary,
weight: 1, Weight: 1,
}, },
} }
} }
@ -65,41 +66,41 @@ func (gui *Gui) getMidSectionWeights() (int, int) {
return sideSectionWeight, mainSectionWeight 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 { if gui.State.Searching.isSearching {
return []*box{ return []*boxlayout.Box{
{ {
viewName: "searchPrefix", ViewName: "searchPrefix",
size: len(SEARCH_PREFIX), Size: len(SEARCH_PREFIX),
}, },
{ {
viewName: "search", ViewName: "search",
weight: 1, Weight: 1,
}, },
} }
} }
result := []*box{} result := []*boxlayout.Box{}
if len(appStatus) > 0 { if len(appStatus) > 0 {
result = append(result, result = append(result,
&box{ &boxlayout.Box{
viewName: "appStatus", ViewName: "appStatus",
size: len(appStatus) + len(INFO_SECTION_PADDING), Size: len(appStatus) + len(INFO_SECTION_PADDING),
}, },
) )
} }
result = append(result, result = append(result,
[]*box{ []*boxlayout.Box{
{ {
viewName: "options", ViewName: "options",
weight: 1, Weight: 1,
}, },
{ {
viewName: "information", ViewName: "information",
// unlike appStatus, informationStr has various colors so we need to decolorise before taking the length // 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 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() width, height := gui.g.Size()
sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights() sideSectionWeight, mainSectionWeight := gui.getMidSectionWeights()
sidePanelsDirection := COLUMN sidePanelsDirection := boxlayout.COLUMN
portraitMode := width <= 84 && height > 45 portraitMode := width <= 84 && height > 45
if portraitMode { if portraitMode {
sidePanelsDirection = ROW sidePanelsDirection = boxlayout.ROW
} }
root := &box{ root := &boxlayout.Box{
direction: ROW, Direction: boxlayout.ROW,
children: []*box{ Children: []*boxlayout.Box{
{ {
direction: sidePanelsDirection, Direction: sidePanelsDirection,
weight: 1, Weight: 1,
children: []*box{ Children: []*boxlayout.Box{
{ {
direction: ROW, Direction: boxlayout.ROW,
weight: sideSectionWeight, Weight: sideSectionWeight,
conditionalChildren: gui.sidePanelChildren, ConditionalChildren: gui.sidePanelChildren,
}, },
{ {
conditionalDirection: func(width int, height int) int { ConditionalDirection: func(width int, height int) int {
mainPanelSplitMode := gui.Config.GetUserConfig().GetString("gui.mainPanelSplitMode") mainPanelSplitMode := gui.Config.GetUserConfig().GetString("gui.mainPanelSplitMode")
switch mainPanelSplitMode { switch mainPanelSplitMode {
case "vertical": case "vertical":
return ROW return boxlayout.ROW
case "horizontal": case "horizontal":
return COLUMN return boxlayout.COLUMN
default: default:
if width < 160 && height > 30 { // 2 80 character width panels if width < 160 && height > 30 { // 2 80 character width panels
return ROW return boxlayout.ROW
} else { } else {
return COLUMN return boxlayout.COLUMN
} }
} }
}, },
direction: COLUMN, Direction: boxlayout.COLUMN,
weight: mainSectionWeight, Weight: mainSectionWeight,
children: gui.mainSectionChildren(), Children: gui.mainSectionChildren(),
}, },
}, },
}, },
{ {
direction: COLUMN, Direction: boxlayout.COLUMN,
size: 1, Size: 1,
children: gui.infoSectionChildren(informationStr, appStatus), 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() currentCyclableViewName := gui.currentCyclableViewName()
if gui.State.ScreenMode == SCREEN_FULL || gui.State.ScreenMode == SCREEN_HALF { 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 { if viewName == currentCyclableViewName {
return &box{ return &boxlayout.Box{
viewName: viewName, ViewName: viewName,
weight: 1, Weight: 1,
} }
} else { } else {
return &box{ return &boxlayout.Box{
viewName: viewName, ViewName: viewName,
size: 0, Size: 0,
} }
} }
} }
return []*box{ return []*boxlayout.Box{
fullHeightBox("status"), fullHeightBox("status"),
fullHeightBox("files"), fullHeightBox("files"),
fullHeightBox("branches"), fullHeightBox("branches"),
@ -191,26 +192,26 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*box {
} }
} else if height >= 28 { } else if height >= 28 {
accordianMode := gui.Config.GetUserConfig().GetBool("gui.expandFocusedSidePanel") accordianMode := gui.Config.GetUserConfig().GetBool("gui.expandFocusedSidePanel")
accordianBox := func(defaultBox *box) *box { accordianBox := func(defaultBox *boxlayout.Box) *boxlayout.Box {
if accordianMode && defaultBox.viewName == currentCyclableViewName { if accordianMode && defaultBox.ViewName == currentCyclableViewName {
return &box{ return &boxlayout.Box{
viewName: defaultBox.viewName, ViewName: defaultBox.ViewName,
weight: 2, Weight: 2,
} }
} }
return defaultBox return defaultBox
} }
return []*box{ return []*boxlayout.Box{
{ {
viewName: "status", ViewName: "status",
size: 3, Size: 3,
}, },
accordianBox(&box{viewName: "files", weight: 1}), accordianBox(&boxlayout.Box{ViewName: "files", Weight: 1}),
accordianBox(&box{viewName: "branches", weight: 1}), accordianBox(&boxlayout.Box{ViewName: "branches", Weight: 1}),
accordianBox(&box{viewName: "commits", weight: 1}), accordianBox(&boxlayout.Box{ViewName: "commits", Weight: 1}),
accordianBox(&box{viewName: "stash", size: 3}), accordianBox(&boxlayout.Box{ViewName: "stash", Size: 3}),
} }
} else { } else {
squashedHeight := 1 squashedHeight := 1
@ -218,21 +219,21 @@ func (gui *Gui) sidePanelChildren(width int, height int) []*box {
squashedHeight = 3 squashedHeight = 3
} }
squashedSidePanelBox := func(viewName string) *box { squashedSidePanelBox := func(viewName string) *boxlayout.Box {
if viewName == currentCyclableViewName { if viewName == currentCyclableViewName {
return &box{ return &boxlayout.Box{
viewName: viewName, ViewName: viewName,
weight: 1, Weight: 1,
} }
} else { } else {
return &box{ return &boxlayout.Box{
viewName: viewName, ViewName: viewName,
size: squashedHeight, Size: squashedHeight,
} }
} }
} }
return []*box{ return []*boxlayout.Box{
squashedSidePanelBox("status"), squashedSidePanelBox("status"),
squashedSidePanelBox("files"), squashedSidePanelBox("files"),
squashedSidePanelBox("branches"), squashedSidePanelBox("branches"),

View File

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

View File

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