From 1e12a60b346f373b53edf5aa3721f9b5e6ee574f Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 15 Aug 2020 08:28:02 +1000 Subject: [PATCH] move box layout stuff into its own package --- pkg/gui/arrangement.go | 155 +++++++++--------- .../{dimensions.go => boxlayout/boxlayout.go} | 94 +++++------ pkg/gui/layout.go | 10 +- 3 files changed, 130 insertions(+), 129 deletions(-) rename pkg/gui/{dimensions.go => boxlayout/boxlayout.go} (56%) diff --git a/pkg/gui/arrangement.go b/pkg/gui/arrangement.go index 9380465a2..25988bbd2 100644 --- a/pkg/gui/arrangement.go +++ b/pkg/gui/arrangement.go @@ -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"), diff --git a/pkg/gui/dimensions.go b/pkg/gui/boxlayout/boxlayout.go similarity index 56% rename from pkg/gui/dimensions.go rename to pkg/gui/boxlayout/boxlayout.go index 912af50d2..383a12ffd 100644 --- a/pkg/gui/dimensions.go +++ b/pkg/gui/boxlayout/boxlayout.go @@ -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 } diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go index 1fa949b48..9f49c0d25 100644 --- a/pkg/gui/layout.go +++ b/pkg/gui/layout.go @@ -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, ) }