mirror of https://github.com/mum4k/termdash.git
Refactor `SplitFixed()` to use SplitCells
* Add basic tests to `SplitFixed()` * Panic when both `SplitFixed()` and `SplitPercent()` are used * Refactor `validateOptions()` into two smaller functions
This commit is contained in:
parent
4aa60fe8e7
commit
70a5255d5e
|
@ -171,10 +171,17 @@ func (c *Container) split() (image.Rectangle, image.Rectangle, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return image.ZR, image.ZR, err
|
return image.ZR, image.ZR, err
|
||||||
}
|
}
|
||||||
if c.opts.split == splitTypeVertical {
|
if c.opts.splitFixed > -1 {
|
||||||
return area.VSplit(ar, c.opts.splitPercent, c.opts.splitFixed)
|
if c.opts.split == splitTypeVertical {
|
||||||
|
return area.VSplitCells(ar, c.opts.splitFixed)
|
||||||
|
}
|
||||||
|
return area.HSplitCells(ar, c.opts.splitFixed)
|
||||||
}
|
}
|
||||||
return area.HSplit(ar, c.opts.splitPercent, c.opts.splitFixed)
|
|
||||||
|
if c.opts.split == splitTypeVertical {
|
||||||
|
return area.VSplit(ar, c.opts.splitPercent)
|
||||||
|
}
|
||||||
|
return area.HSplit(ar, c.opts.splitPercent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFirst creates and returns the first sub container of this container.
|
// createFirst creates and returns the first sub container of this container.
|
||||||
|
|
|
@ -490,6 +490,26 @@ func TestNew(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantContainerErr: true,
|
wantContainerErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "fails when both SplitFixed and SplitPercent are specified",
|
||||||
|
termSize: image.Point{10, 10},
|
||||||
|
container: func(ft *faketerm.Terminal) (*Container, error) {
|
||||||
|
return New(
|
||||||
|
ft,
|
||||||
|
SplitHorizontal(
|
||||||
|
Top(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
Bottom(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
SplitFixed(4),
|
||||||
|
SplitPercent(20),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
wantContainerErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "empty container",
|
desc: "empty container",
|
||||||
termSize: image.Point{10, 10},
|
termSize: image.Point{10, 10},
|
||||||
|
@ -616,6 +636,32 @@ func TestNew(t *testing.T) {
|
||||||
return ft
|
return ft
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "horizontal unequal split",
|
||||||
|
termSize: image.Point{10, 20},
|
||||||
|
container: func(ft *faketerm.Terminal) (*Container, error) {
|
||||||
|
return New(
|
||||||
|
ft,
|
||||||
|
SplitHorizontal(
|
||||||
|
Top(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
Bottom(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
SplitFixed(4),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
want: func(size image.Point) *faketerm.Terminal {
|
||||||
|
ft := faketerm.MustNew(size)
|
||||||
|
cvs := testcanvas.MustNew(ft.Area())
|
||||||
|
testdraw.MustBorder(cvs, image.Rect(0, 0, 10, 4))
|
||||||
|
testdraw.MustBorder(cvs, image.Rect(0, 4, 10, 20))
|
||||||
|
testcanvas.MustApply(cvs, ft)
|
||||||
|
return ft
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "horizontal split, parent and children have borders",
|
desc: "horizontal split, parent and children have borders",
|
||||||
termSize: image.Point{10, 10},
|
termSize: image.Point{10, 10},
|
||||||
|
@ -742,6 +788,32 @@ func TestNew(t *testing.T) {
|
||||||
return ft
|
return ft
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "vertical fixed splits",
|
||||||
|
termSize: image.Point{20, 10},
|
||||||
|
container: func(ft *faketerm.Terminal) (*Container, error) {
|
||||||
|
return New(
|
||||||
|
ft,
|
||||||
|
SplitVertical(
|
||||||
|
Left(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
Right(
|
||||||
|
Border(linestyle.Light),
|
||||||
|
),
|
||||||
|
SplitFixed(4),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
want: func(size image.Point) *faketerm.Terminal {
|
||||||
|
ft := faketerm.MustNew(size)
|
||||||
|
cvs := testcanvas.MustNew(ft.Area())
|
||||||
|
testdraw.MustBorder(cvs, image.Rect(0, 0, 4, 10))
|
||||||
|
testdraw.MustBorder(cvs, image.Rect(4, 0, 20, 10))
|
||||||
|
testcanvas.MustApply(cvs, ft)
|
||||||
|
return ft
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "vertical split, parent and children have borders",
|
desc: "vertical split, parent and children have borders",
|
||||||
termSize: image.Point{10, 10},
|
termSize: image.Point{10, 10},
|
||||||
|
|
|
@ -127,7 +127,7 @@ func mirror() *fakewidget.Mirror {
|
||||||
|
|
||||||
// mustHSplit splits the area or panics.
|
// mustHSplit splits the area or panics.
|
||||||
func mustHSplit(ar image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle) {
|
func mustHSplit(ar image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle) {
|
||||||
t, b, err := area.HSplit(ar, heightPerc, -1)
|
t, b, err := area.HSplit(ar, heightPerc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ func mustHSplit(ar image.Rectangle, heightPerc int) (top image.Rectangle, bottom
|
||||||
|
|
||||||
// mustVSplit splits the area or panics.
|
// mustVSplit splits the area or panics.
|
||||||
func mustVSplit(ar image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle) {
|
func mustVSplit(ar image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle) {
|
||||||
l, r, err := area.VSplit(ar, widthPerc, -1)
|
l, r, err := area.VSplit(ar, widthPerc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,8 @@ func applyOptions(c *Container, opts ...Option) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateOptions validates options set in the container tree.
|
// ensure all the container identifiers are either empty or unique.
|
||||||
func validateOptions(c *Container) error {
|
func validateIds(c *Container) error {
|
||||||
// ensure all the container identifiers are either empty or unique.
|
|
||||||
var errStr string
|
var errStr string
|
||||||
seenID := map[string]bool{}
|
seenID := map[string]bool{}
|
||||||
preOrder(c, &errStr, func(c *Container) error {
|
preOrder(c, &errStr, func(c *Container) error {
|
||||||
|
@ -60,6 +59,43 @@ func validateOptions(c *Container) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure all the container only have one split modifier.
|
||||||
|
func validateSplits(c *Container) error {
|
||||||
|
var errStr string
|
||||||
|
preOrder(c, &errStr, func(c *Container) error {
|
||||||
|
if c.opts.splitFixed > -1 && c.opts.splitPercent != 50 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"only one of splitFixed `%v` and splitPercent `%v` is allowed to be set per container",
|
||||||
|
c.opts.splitFixed,
|
||||||
|
c.opts.splitPercent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if errStr != "" {
|
||||||
|
return errors.New(errStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateOptions validates options set in the container tree.
|
||||||
|
func validateOptions(c *Container) error {
|
||||||
|
// ensure all the container identifiers are either empty or unique.
|
||||||
|
errIds := validateIds(c)
|
||||||
|
if errIds != nil {
|
||||||
|
return errIds
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that max one split modifier is used per container
|
||||||
|
errSplits := validateSplits(c)
|
||||||
|
if errSplits != nil {
|
||||||
|
return errSplits
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Option is used to provide options to a container.
|
// Option is used to provide options to a container.
|
||||||
type Option interface {
|
type Option interface {
|
||||||
// set sets the provided option.
|
// set sets the provided option.
|
||||||
|
@ -218,8 +254,8 @@ func SplitPercent(p int) SplitOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitFixed sets the heights of the top and the bottom containers.
|
// SplitFixed sets the size as a fixed value of the available space.
|
||||||
// The first container will be fixed and the second one will fill the rest of the space.
|
// FIXME: More comments
|
||||||
// Allows values greater or equal to zero.
|
// Allows values greater or equal to zero.
|
||||||
func SplitFixed(cells int) SplitOption {
|
func SplitFixed(cells int) SplitOption {
|
||||||
return splitOption(func(opts *options) error {
|
return splitOption(func(opts *options) error {
|
||||||
|
|
|
@ -45,23 +45,14 @@ func FromSize(size image.Point) (image.Rectangle, error) {
|
||||||
//
|
//
|
||||||
// The fixed value must by in the range -1 <= heightFixed.
|
// The fixed value must by in the range -1 <= heightFixed.
|
||||||
// If fixed value is -1 (default value), heightPerc is used instead.
|
// If fixed value is -1 (default value), heightPerc is used instead.
|
||||||
func HSplit(area image.Rectangle, heightPerc int, heightFixed int) (top image.Rectangle, bottom image.Rectangle, err error) {
|
func HSplit(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) {
|
||||||
if min, max := 0, 100; heightPerc < min || heightPerc > max {
|
if min, max := 0, 100; heightPerc < min || heightPerc > max {
|
||||||
return image.ZR, image.ZR, fmt.Errorf("invalid heightPerc %d, must be in range %d <= heightPerc <= %d", heightPerc, min, max)
|
return image.ZR, image.ZR, fmt.Errorf("invalid heightPerc %d, must be in range %d <= heightPerc <= %d", heightPerc, min, max)
|
||||||
}
|
}
|
||||||
if heightFixed < -1 {
|
|
||||||
return image.ZR, image.ZR, fmt.Errorf("invalid heightFixed %d, must be in range %d <= heightFixed", heightFixed, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prioritize `SplitFixed()` over `SplitPercent()`.
|
height := area.Dy() * heightPerc / 100
|
||||||
if heightFixed >= 0 {
|
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height)
|
||||||
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+heightFixed)
|
bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y)
|
||||||
bottom = image.Rect(area.Min.X, area.Min.Y+heightFixed, area.Max.X, area.Max.Y)
|
|
||||||
} else {
|
|
||||||
height := area.Dy() * heightPerc / 100
|
|
||||||
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height)
|
|
||||||
bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y)
|
|
||||||
}
|
|
||||||
if top.Dy() == 0 {
|
if top.Dy() == 0 {
|
||||||
top = image.ZR
|
top = image.ZR
|
||||||
}
|
}
|
||||||
|
@ -78,23 +69,14 @@ func HSplit(area image.Rectangle, heightPerc int, heightFixed int) (top image.Re
|
||||||
//
|
//
|
||||||
// The fixed value must by in the range -1 <= heightFixed.
|
// The fixed value must by in the range -1 <= heightFixed.
|
||||||
// If fixed value is -1 (default value), heightPerc is used instead.
|
// If fixed value is -1 (default value), heightPerc is used instead.
|
||||||
func VSplit(area image.Rectangle, widthPerc int, widthFixed int) (left image.Rectangle, right image.Rectangle, err error) {
|
func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) {
|
||||||
if min, max := 0, 100; widthPerc < min || widthPerc > max {
|
if min, max := 0, 100; widthPerc < min || widthPerc > max {
|
||||||
return image.ZR, image.ZR, fmt.Errorf("invalid widthPerc %d, must be in range %d <= widthPerc <= %d", widthPerc, min, max)
|
return image.ZR, image.ZR, fmt.Errorf("invalid widthPerc %d, must be in range %d <= widthPerc <= %d", widthPerc, min, max)
|
||||||
}
|
}
|
||||||
if widthFixed < -1 {
|
|
||||||
return image.ZR, image.ZR, fmt.Errorf("invalid widthFixed %d, must be in range %d <= widthFixed", widthFixed, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prioritize `SplitFixed()` over `SplitPercent()`.
|
width := area.Dx() * widthPerc / 100
|
||||||
if widthFixed >= 0 {
|
left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y)
|
||||||
left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+widthFixed, area.Max.Y)
|
right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y)
|
||||||
right = image.Rect(area.Min.X+widthFixed, area.Min.Y, area.Max.X, area.Max.Y)
|
|
||||||
} else {
|
|
||||||
width := area.Dx() * widthPerc / 100
|
|
||||||
left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y)
|
|
||||||
right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y)
|
|
||||||
}
|
|
||||||
if left.Dx() == 0 {
|
if left.Dx() == 0 {
|
||||||
left = image.ZR
|
left = image.ZR
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ func TestHSplit(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
gotTop, gotBot, err := HSplit(tc.area, tc.heightPerc, -1)
|
gotTop, gotBot, err := HSplit(tc.area, tc.heightPerc)
|
||||||
if (err != nil) != tc.wantErr {
|
if (err != nil) != tc.wantErr {
|
||||||
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
|
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@ func TestVSplit(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
gotLeft, gotRight, err := VSplit(tc.area, tc.widthPerc, -1)
|
gotLeft, gotRight, err := VSplit(tc.area, tc.widthPerc)
|
||||||
if (err != nil) != tc.wantErr {
|
if (err != nil) != tc.wantErr {
|
||||||
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
|
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,7 +330,7 @@ func split(cvsAr image.Rectangle, label string, widthPerc *int) (labelAr, textAr
|
||||||
switch {
|
switch {
|
||||||
case widthPerc != nil:
|
case widthPerc != nil:
|
||||||
splitP := 100 - *widthPerc
|
splitP := 100 - *widthPerc
|
||||||
labelAr, textAr, err := area.VSplit(cvsAr, splitP, -1)
|
labelAr, textAr, err := area.VSplit(cvsAr, splitP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return image.ZR, image.ZR, err
|
return image.ZR, image.ZR, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue