mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-05 00:59:19 +02:00
better weight distribution in window arrangement
This commit is contained in:
4
go.mod
4
go.mod
@ -16,7 +16,7 @@ require (
|
|||||||
github.com/integrii/flaggy v1.4.0
|
github.com/integrii/flaggy v1.4.0
|
||||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67
|
github.com/jesseduffield/gocui v0.3.1-0.20220417002912-bce22fd599f6
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||||
@ -40,7 +40,7 @@ require (
|
|||||||
github.com/emirpasic/gods v1.12.0 // indirect
|
github.com/emirpasic/gods v1.12.0 // indirect
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
github.com/fatih/color v1.9.0 // indirect
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/gdamore/tcell/v2 v2.5.0 // indirect
|
github.com/gdamore/tcell/v2 v2.5.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.0 // indirect
|
github.com/go-git/gcfg v1.5.0 // indirect
|
||||||
github.com/go-git/go-billy/v5 v5.0.0 // indirect
|
github.com/go-git/go-billy/v5 v5.0.0 // indirect
|
||||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||||
|
8
go.sum
8
go.sum
@ -33,8 +33,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||||
github.com/gdamore/tcell/v2 v2.5.0 h1:/LA5f/wqTP5mWT79czngibKVVx5wOgdFTIXPQ68fMO8=
|
github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I=
|
||||||
github.com/gdamore/tcell/v2 v2.5.0/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
|
||||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
@ -70,8 +70,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
|||||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67 h1:6NIOoR4LMuNEkP6e9P6GVZkzgYZ7rqpfM+LieKqubnA=
|
github.com/jesseduffield/gocui v0.3.1-0.20220417002912-bce22fd599f6 h1:Fmay0Lz21taUpXiIbFkjjIIcn0E5GKwp5UFRuXaOiGQ=
|
||||||
github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
github.com/jesseduffield/gocui v0.3.1-0.20220417002912-bce22fd599f6/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=
|
||||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e/go.mod h1:u60qdFGXRd36jyEXxetz0vQceQIxzI13lIo3EFUDf4I=
|
||||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package boxlayout
|
package boxlayout
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"github.com/jesseduffield/generics/slices"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
type Dimensions struct {
|
type Dimensions struct {
|
||||||
X0 int
|
X0 int
|
||||||
@ -69,45 +73,12 @@ func ArrangeWindows(root *Box, x0, y0, width, height int) map[string]Dimensions
|
|||||||
availableSize = height
|
availableSize = height
|
||||||
}
|
}
|
||||||
|
|
||||||
// work out size taken up by children
|
sizes := calcSizes(children, availableSize)
|
||||||
reservedSize := 0
|
|
||||||
totalWeight := 0
|
|
||||||
for _, child := range children {
|
|
||||||
// assuming either size or weight are non-zero
|
|
||||||
reservedSize += child.Size
|
|
||||||
totalWeight += child.Weight
|
|
||||||
}
|
|
||||||
|
|
||||||
remainingSize := availableSize - reservedSize
|
|
||||||
if remainingSize < 0 {
|
|
||||||
remainingSize = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
unitSize := 0
|
|
||||||
extraSize := 0
|
|
||||||
if totalWeight > 0 {
|
|
||||||
unitSize = remainingSize / totalWeight
|
|
||||||
extraSize = remainingSize % totalWeight
|
|
||||||
}
|
|
||||||
|
|
||||||
result := map[string]Dimensions{}
|
result := map[string]Dimensions{}
|
||||||
offset := 0
|
offset := 0
|
||||||
for _, child := range children {
|
for i, child := range children {
|
||||||
var boxSize int
|
boxSize := sizes[i]
|
||||||
if child.isStatic() {
|
|
||||||
boxSize = child.Size
|
|
||||||
// assuming that only one static child can have a size greater than the
|
|
||||||
// available space. In that case we just crop the size to what's available
|
|
||||||
if boxSize > availableSize {
|
|
||||||
boxSize = availableSize
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: consider more evenly distributing the remainder
|
|
||||||
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 {
|
if direction == COLUMN {
|
||||||
@ -123,6 +94,95 @@ func ArrangeWindows(root *Box, x0, y0, width, height int) map[string]Dimensions
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcSizes(boxes []*Box, availableSpace int) []int {
|
||||||
|
normalizedWeights := normalizeWeights(slices.Map(boxes, func(box *Box) int { return box.Weight }))
|
||||||
|
|
||||||
|
totalWeight := 0
|
||||||
|
reservedSpace := 0
|
||||||
|
for i, box := range boxes {
|
||||||
|
if box.isStatic() {
|
||||||
|
reservedSpace += box.Size
|
||||||
|
} else {
|
||||||
|
totalWeight += normalizedWeights[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicSpace := utils.Max(0, availableSpace-reservedSpace)
|
||||||
|
|
||||||
|
unitSize := 0
|
||||||
|
extraSpace := 0
|
||||||
|
if totalWeight > 0 {
|
||||||
|
unitSize = dynamicSpace / totalWeight
|
||||||
|
extraSpace = dynamicSpace % totalWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]int, len(boxes))
|
||||||
|
for i, box := range boxes {
|
||||||
|
if box.isStatic() {
|
||||||
|
// assuming that only one static child can have a size greater than the
|
||||||
|
// available space. In that case we just crop the size to what's available
|
||||||
|
result[i] = utils.Min(availableSpace, box.Size)
|
||||||
|
} else {
|
||||||
|
result[i] = unitSize * normalizedWeights[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// distribute the remainder across dynamic boxes.
|
||||||
|
for extraSpace > 0 {
|
||||||
|
for i, weight := range normalizedWeights {
|
||||||
|
if weight > 0 {
|
||||||
|
result[i]++
|
||||||
|
extraSpace--
|
||||||
|
normalizedWeights[i]--
|
||||||
|
|
||||||
|
if extraSpace == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes common multiple from weights e.g. if we get 2, 4, 4 we return 1, 2, 2.
|
||||||
|
func normalizeWeights(weights []int) []int {
|
||||||
|
if len(weights) == 0 {
|
||||||
|
return []int{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to spare us some computation we'll exit early if any of our weights is 1
|
||||||
|
if slices.Some(weights, func(weight int) bool { return weight == 1 }) {
|
||||||
|
return weights
|
||||||
|
}
|
||||||
|
|
||||||
|
// map weights to factorSlices and find the lowest common factor
|
||||||
|
positiveWeights := slices.Filter(weights, func(weight int) bool { return weight > 0 })
|
||||||
|
factorSlices := slices.Map(positiveWeights, func(weight int) []int { return calcFactors(weight) })
|
||||||
|
commonFactors := factorSlices[0]
|
||||||
|
for _, factors := range factorSlices {
|
||||||
|
commonFactors = lo.Intersect(commonFactors, factors)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(commonFactors) == 0 {
|
||||||
|
return weights
|
||||||
|
}
|
||||||
|
|
||||||
|
newWeights := slices.Map(weights, func(weight int) int { return weight / commonFactors[0] })
|
||||||
|
|
||||||
|
return normalizeWeights(newWeights)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcFactors(n int) []int {
|
||||||
|
factors := []int{}
|
||||||
|
for i := 2; i <= n; i++ {
|
||||||
|
if n%i == 0 {
|
||||||
|
factors = append(factors, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return factors
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Box) isStatic() bool {
|
func (b *Box) isStatic() bool {
|
||||||
return b.Size > 0
|
return b.Size > 0
|
||||||
}
|
}
|
||||||
|
@ -19,24 +19,24 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"Empty box",
|
testName: "Empty box",
|
||||||
&Box{},
|
root: &Box{},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
10,
|
width: 10,
|
||||||
10,
|
height: 10,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(t, result, map[string]Dimensions{})
|
assert.EqualValues(t, result, map[string]Dimensions{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with static and dynamic panel",
|
testName: "Box with static and dynamic panel",
|
||||||
&Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic"}}},
|
root: &Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic"}}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
10,
|
width: 10,
|
||||||
10,
|
height: 10,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -48,13 +48,13 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with static and two dynamic panels",
|
testName: "Box with static and two dynamic panels",
|
||||||
&Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
root: &Box{Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
10,
|
width: 10,
|
||||||
10,
|
height: 10,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -67,13 +67,13 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with COLUMN direction",
|
testName: "Box with COLUMN direction",
|
||||||
&Box{Direction: COLUMN, Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 1, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
10,
|
width: 10,
|
||||||
10,
|
height: 10,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -86,19 +86,19 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with COLUMN direction only on wide boxes with narrow box",
|
testName: "Box with COLUMN direction only on wide boxes with narrow box",
|
||||||
&Box{ConditionalDirection: func(width int, height int) Direction {
|
root: &Box{ConditionalDirection: func(width int, height int) Direction {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return COLUMN
|
return COLUMN
|
||||||
} else {
|
} else {
|
||||||
return ROW
|
return ROW
|
||||||
}
|
}
|
||||||
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
4,
|
width: 4,
|
||||||
4,
|
height: 4,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -110,19 +110,20 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with COLUMN direction only on wide boxes with wide box",
|
testName: "Box with COLUMN direction only on wide boxes with wide box",
|
||||||
&Box{ConditionalDirection: func(width int, height int) Direction {
|
root: &Box{ConditionalDirection: func(width int, height int) Direction {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return COLUMN
|
return COLUMN
|
||||||
} else {
|
} else {
|
||||||
return ROW
|
return ROW
|
||||||
}
|
}
|
||||||
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
}, Children: []*Box{{Weight: 1, Window: "dynamic1"}, {Weight: 1, Window: "dynamic2"}}},
|
||||||
0,
|
// 5 / 2 = 2 remainder 1. That remainder goes to the first box.
|
||||||
0,
|
x0: 0,
|
||||||
5,
|
y0: 0,
|
||||||
5,
|
width: 5,
|
||||||
func(result map[string]Dimensions) {
|
height: 5,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -134,19 +135,19 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with conditional children where box is wide",
|
testName: "Box with conditional children where box is wide",
|
||||||
&Box{ConditionalChildren: func(width int, height int) []*Box {
|
root: &Box{ConditionalChildren: func(width int, height int) []*Box {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return []*Box{{Window: "wide", Weight: 1}}
|
return []*Box{{Window: "wide", Weight: 1}}
|
||||||
} else {
|
} else {
|
||||||
return []*Box{{Window: "narrow", Weight: 1}}
|
return []*Box{{Window: "narrow", Weight: 1}}
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
5,
|
width: 5,
|
||||||
5,
|
height: 5,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -157,19 +158,19 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with conditional children where box is narrow",
|
testName: "Box with conditional children where box is narrow",
|
||||||
&Box{ConditionalChildren: func(width int, height int) []*Box {
|
root: &Box{ConditionalChildren: func(width int, height int) []*Box {
|
||||||
if width > 4 {
|
if width > 4 {
|
||||||
return []*Box{{Window: "wide", Weight: 1}}
|
return []*Box{{Window: "wide", Weight: 1}}
|
||||||
} else {
|
} else {
|
||||||
return []*Box{{Window: "narrow", Weight: 1}}
|
return []*Box{{Window: "narrow", Weight: 1}}
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
4,
|
width: 4,
|
||||||
4,
|
height: 4,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -180,13 +181,13 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Box with static child with size too large",
|
testName: "Box with static child with size too large",
|
||||||
&Box{Direction: COLUMN, Children: []*Box{{Size: 11, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 11, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
||||||
0,
|
x0: 0,
|
||||||
0,
|
y0: 0,
|
||||||
10,
|
width: 10,
|
||||||
10,
|
height: 10,
|
||||||
func(result map[string]Dimensions) {
|
test: func(result map[string]Dimensions) {
|
||||||
assert.EqualValues(
|
assert.EqualValues(
|
||||||
t,
|
t,
|
||||||
result,
|
result,
|
||||||
@ -200,6 +201,118 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// 10 total space minus 2 from the status box leaves us with 8.
|
||||||
|
// Total weight is 3, 8 / 3 = 2 with 2 remainder.
|
||||||
|
// We want to end up with 2, 3, 5 (one unit from remainder to each dynamic box)
|
||||||
|
testName: "Distributing remainder across weighted boxes",
|
||||||
|
root: &Box{Direction: COLUMN, Children: []*Box{{Size: 2, Window: "static"}, {Weight: 1, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}}},
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
|
assert.EqualValues(
|
||||||
|
t,
|
||||||
|
result,
|
||||||
|
map[string]Dimensions{
|
||||||
|
"static": {X0: 0, X1: 1, Y0: 0, Y1: 9}, // 2
|
||||||
|
"dynamic1": {X0: 2, X1: 4, Y0: 0, Y1: 9}, // 3
|
||||||
|
"dynamic2": {X0: 5, X1: 9, Y0: 0, Y1: 9}, // 5
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 9 total space.
|
||||||
|
// total weight is 5, 9 / 5 = 1 with 4 remainder
|
||||||
|
// we want to give 2 of that remainder to the first, 1 to the second, and 1 to the last.
|
||||||
|
// Reason being that we just give units to each box evenly and consider weight in subsequent passes.
|
||||||
|
testName: "Distributing remainder across weighted boxes 2",
|
||||||
|
root: &Box{Direction: COLUMN, Children: []*Box{{Weight: 2, Window: "dynamic1"}, {Weight: 2, Window: "dynamic2"}, {Weight: 1, Window: "dynamic3"}}},
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
width: 9,
|
||||||
|
height: 10,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
|
assert.EqualValues(
|
||||||
|
t,
|
||||||
|
result,
|
||||||
|
map[string]Dimensions{
|
||||||
|
"dynamic1": {X0: 0, X1: 3, Y0: 0, Y1: 9}, // 4
|
||||||
|
"dynamic2": {X0: 4, X1: 6, Y0: 0, Y1: 9}, // 3
|
||||||
|
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 9 total space.
|
||||||
|
// total weight is 5, 9 / 5 = 1 with 4 remainder
|
||||||
|
// we want to give 2 of that remainder to the first, 1 to the second, and 1 to the last.
|
||||||
|
// Reason being that we just give units to each box evenly and consider weight in subsequent passes.
|
||||||
|
testName: "Distributing remainder across weighted boxes with unnormalized weights",
|
||||||
|
root: &Box{Direction: COLUMN, Children: []*Box{{Weight: 4, Window: "dynamic1"}, {Weight: 4, Window: "dynamic2"}, {Weight: 2, Window: "dynamic3"}}},
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
width: 9,
|
||||||
|
height: 10,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
|
assert.EqualValues(
|
||||||
|
t,
|
||||||
|
result,
|
||||||
|
map[string]Dimensions{
|
||||||
|
"dynamic1": {X0: 0, X1: 3, Y0: 0, Y1: 9}, // 4
|
||||||
|
"dynamic2": {X0: 4, X1: 6, Y0: 0, Y1: 9}, // 3
|
||||||
|
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Another distribution test",
|
||||||
|
root: &Box{Direction: COLUMN, Children: []*Box{
|
||||||
|
{Weight: 3, Window: "dynamic1"},
|
||||||
|
{Weight: 1, Window: "dynamic2"},
|
||||||
|
{Weight: 1, Window: "dynamic3"},
|
||||||
|
}},
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
width: 9,
|
||||||
|
height: 10,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
|
assert.EqualValues(
|
||||||
|
t,
|
||||||
|
result,
|
||||||
|
map[string]Dimensions{
|
||||||
|
"dynamic1": {X0: 0, X1: 4, Y0: 0, Y1: 9}, // 5
|
||||||
|
"dynamic2": {X0: 5, X1: 6, Y0: 0, Y1: 9}, // 2
|
||||||
|
"dynamic3": {X0: 7, X1: 8, Y0: 0, Y1: 9}, // 2
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Box with zero weight",
|
||||||
|
root: &Box{Direction: COLUMN, Children: []*Box{
|
||||||
|
{Weight: 1, Window: "dynamic1"},
|
||||||
|
{Weight: 0, Window: "dynamic2"},
|
||||||
|
}},
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
test: func(result map[string]Dimensions) {
|
||||||
|
assert.EqualValues(
|
||||||
|
t,
|
||||||
|
result,
|
||||||
|
map[string]Dimensions{
|
||||||
|
"dynamic1": {X0: 0, X1: 9, Y0: 0, Y1: 9},
|
||||||
|
"dynamic2": {X0: 10, X1: 9, Y0: 0, Y1: 9}, // when X0 > X1, we will hide the window
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
@ -209,3 +322,59 @@ func TestArrangeWindows(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNormalizeWeights(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
testName string
|
||||||
|
input []int
|
||||||
|
expected []int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testName: "empty",
|
||||||
|
input: []int{},
|
||||||
|
expected: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "one item of value 1",
|
||||||
|
input: []int{1},
|
||||||
|
expected: []int{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "one item of value greater than 1",
|
||||||
|
input: []int{2},
|
||||||
|
expected: []int{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "slice contains 1",
|
||||||
|
input: []int{2, 1},
|
||||||
|
expected: []int{2, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "slice contains 2 and 2",
|
||||||
|
input: []int{2, 2},
|
||||||
|
expected: []int{1, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "no common multiple",
|
||||||
|
input: []int{2, 3},
|
||||||
|
expected: []int{2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "complex case",
|
||||||
|
input: []int{10, 10, 20},
|
||||||
|
expected: []int{1, 1, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "when a zero weight is included it is ignored",
|
||||||
|
input: []int{10, 10, 20, 0},
|
||||||
|
expected: []int{1, 1, 2, 0},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
assert.EqualValues(t, s.expected, normalizeWeights(s.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1
vendor/github.com/gdamore/tcell/v2/tscreen.go
generated
vendored
1
vendor/github.com/gdamore/tcell/v2/tscreen.go
generated
vendored
@ -858,6 +858,7 @@ func (t *tScreen) Show() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *tScreen) clearScreen() {
|
func (t *tScreen) clearScreen() {
|
||||||
|
t.TPuts(t.ti.AttrOff)
|
||||||
fg, bg, _ := t.style.Decompose()
|
fg, bg, _ := t.style.Decompose()
|
||||||
t.sendFgBg(fg, bg)
|
t.sendFgBg(fg, bg)
|
||||||
t.TPuts(t.ti.Clear)
|
t.TPuts(t.ti.Clear)
|
||||||
|
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
2
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -1053,7 +1053,7 @@ func (g *Gui) draw(v *View) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !v.Visible || v.y1 < v.y0 {
|
if !v.Visible || v.y1 < v.y0 || v.x1 < v.x0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -36,7 +36,7 @@ github.com/fsnotify/fsnotify
|
|||||||
# github.com/gdamore/encoding v1.0.0
|
# github.com/gdamore/encoding v1.0.0
|
||||||
## explicit; go 1.9
|
## explicit; go 1.9
|
||||||
github.com/gdamore/encoding
|
github.com/gdamore/encoding
|
||||||
# github.com/gdamore/tcell/v2 v2.5.0
|
# github.com/gdamore/tcell/v2 v2.5.1
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/gdamore/tcell/v2
|
github.com/gdamore/tcell/v2
|
||||||
github.com/gdamore/tcell/v2/terminfo
|
github.com/gdamore/tcell/v2/terminfo
|
||||||
@ -169,7 +169,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
|
|||||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
||||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
||||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
||||||
# github.com/jesseduffield/gocui v0.3.1-0.20220416053910-5b19e175bc67
|
# github.com/jesseduffield/gocui v0.3.1-0.20220417002912-bce22fd599f6
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/jesseduffield/gocui
|
github.com/jesseduffield/gocui
|
||||||
# github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
# github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||||
|
Reference in New Issue
Block a user