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:
parent
8430b04492
commit
1e12a60b34
@ -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"),
|
||||||
|
@ -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
|
||||||
}
|
}
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user