mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
Making the container API easier to use.
This commit is contained in:
parent
af6c5e9c81
commit
1911e2190a
@ -2,7 +2,7 @@
|
||||
Package container defines a type that wraps other containers or widgets.
|
||||
|
||||
The container supports splitting container into sub containers, defining
|
||||
container styles and placing widgets. The container also creates and manages
|
||||
container styles and placing widgets. The container also creates and manages
|
||||
canvases assigned to the placed widgets.
|
||||
*/
|
||||
package container
|
||||
@ -47,50 +47,27 @@ func (c *Container) String() string {
|
||||
// New returns a new root container that will use the provided terminal and
|
||||
// applies the provided options.
|
||||
func New(t terminalapi.Terminal, opts ...Option) *Container {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt.set(o)
|
||||
}
|
||||
|
||||
size := t.Size()
|
||||
return &Container{
|
||||
root := &Container{
|
||||
term: t,
|
||||
// The root container has access to the entire terminal.
|
||||
area: image.Rect(0, 0, size.X, size.Y),
|
||||
opts: o,
|
||||
opts: &options{},
|
||||
}
|
||||
applyOptions(root, opts...)
|
||||
return root
|
||||
}
|
||||
|
||||
// newChild creates a new child container of the given parent.
|
||||
func newChild(parent *Container, area image.Rectangle, opts ...Option) *Container {
|
||||
o := &options{}
|
||||
for _, opt := range opts {
|
||||
opt.set(o)
|
||||
}
|
||||
|
||||
func newChild(parent *Container, area image.Rectangle) *Container {
|
||||
return &Container{
|
||||
parent: parent,
|
||||
term: parent.term,
|
||||
area: area,
|
||||
opts: o,
|
||||
opts: &options{},
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the parent container of this container and applies the provided
|
||||
// options to the parent container. Returns nil if this container is the root
|
||||
// of the container tree.
|
||||
func (c *Container) Parent(opts ...Option) *Container {
|
||||
if c.parent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := c.parent
|
||||
for _, opt := range opts {
|
||||
opt.set(p.opts)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// hasBorder determines if this container has a border.
|
||||
func (c *Container) hasBorder() bool {
|
||||
return c.opts.border != draw.LineStyleNone
|
||||
@ -107,75 +84,29 @@ func (c *Container) usable() image.Rectangle {
|
||||
}
|
||||
|
||||
// split splits the container's usable area into child areas.
|
||||
// Panics if the container isn't configured for a split.
|
||||
func (c *Container) split() (image.Rectangle, image.Rectangle) {
|
||||
if ar := c.usable(); c.opts.split == splitTypeHorizontal {
|
||||
return area.HSplit(ar)
|
||||
} else {
|
||||
if ar := c.usable(); c.opts.split == splitTypeVertical {
|
||||
return area.VSplit(ar)
|
||||
} else {
|
||||
return area.HSplit(ar)
|
||||
}
|
||||
}
|
||||
|
||||
// First returns the first sub container of this container.
|
||||
// This is the left sub container when using SplitVertical() or the top sub
|
||||
// container when using SplitHorizontal().
|
||||
// If this container doesn't have the first sub container yet, it will be
|
||||
// created. Applies the provided options to the first sub container.
|
||||
// Returns nil if this container contains a widget, containers with widgets
|
||||
// cannot have sub containers.
|
||||
func (c *Container) First(opts ...Option) *Container {
|
||||
if c.opts.widget != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child := c.first; child != nil {
|
||||
for _, opt := range opts {
|
||||
opt.set(child.opts)
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
// createFirst creates and returns the first sub container of this container.
|
||||
func (c *Container) createFirst() *Container {
|
||||
ar, _ := c.split()
|
||||
c.first = newChild(c, ar, opts...)
|
||||
c.first = newChild(c, ar)
|
||||
return c.first
|
||||
}
|
||||
|
||||
// Second returns the second sub container of this container.
|
||||
// This is the left sub container when using SplitVertical() or the top sub
|
||||
// container when using SplitHorizontal().
|
||||
// If this container doesn't have the second sub container yet, it will be
|
||||
// created. Applies the provided options to the second sub container.
|
||||
// Returns nil if this container contains a widget, containers with widgets
|
||||
// cannot have sub containers.
|
||||
func (c *Container) Second(opts ...Option) *Container {
|
||||
if c.opts.widget != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if child := c.second; child != nil {
|
||||
for _, opt := range opts {
|
||||
opt.set(child.opts)
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
// createSecond creates and returns the second sub container of this container.
|
||||
func (c *Container) createSecond() *Container {
|
||||
_, ar := c.split()
|
||||
c.second = newChild(c, ar, opts...)
|
||||
c.second = newChild(c, ar)
|
||||
return c.second
|
||||
}
|
||||
|
||||
// Root returns the root container and applies the provided options to the root
|
||||
// container.
|
||||
func (c *Container) Root(opts ...Option) *Container {
|
||||
for p := c.Parent(); p != nil; p = c.Parent() {
|
||||
c = p
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt.set(c.opts)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// draw draws this container and its widget.
|
||||
// TODO(mum4k): Draw the widget.
|
||||
func (c *Container) draw() error {
|
||||
|
@ -13,55 +13,34 @@ import (
|
||||
|
||||
// Example demonstrates how to use the Container API.
|
||||
func Example() {
|
||||
New( // Create the root container.
|
||||
New(
|
||||
/* terminal = */ nil,
|
||||
SplitHorizontal(),
|
||||
).First( // This is the top half part of the terminal.
|
||||
SplitVertical(),
|
||||
).First( // Left side on the top.
|
||||
VerticalAlignTop(),
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
).Parent().Second( // Right side on the top.
|
||||
HorizontalAlignRight(),
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
).Root().Second( // Bottom half of the terminal.
|
||||
PlaceWidget( /* widget = */ nil),
|
||||
).Root()
|
||||
// TODO(mum4k): Don't require .Root() at the end.
|
||||
SplitVertical(
|
||||
Left(
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Right(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
// TODO(mum4k): Allow splits on different ratios.
|
||||
}
|
||||
|
||||
func TestParentAndRoot(t *testing.T) {
|
||||
ft := faketerm.MustNew(image.Point{1, 1})
|
||||
tests := []struct {
|
||||
desc string
|
||||
container *Container
|
||||
// Arg is the container defined above.
|
||||
want func(c *Container) *Container
|
||||
}{
|
||||
{
|
||||
desc: "root container has no parent",
|
||||
container: New(ft),
|
||||
want: func(c *Container) *Container {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "returns the parent",
|
||||
container: New(ft).First(),
|
||||
want: func(c *Container) *Container {
|
||||
return c.Root()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
if got := tc.container.Parent(); got != tc.want(tc.container) {
|
||||
t.Errorf("Parent => unexpected container\n got: %v\n want: %v", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
// TODO(mum4k): Include an example with a widget.
|
||||
}
|
||||
|
||||
// mustCanvas returns a new canvas or panics.
|
||||
@ -95,145 +74,166 @@ func TestDraw(t *testing.T) {
|
||||
want func(size image.Point) *faketerm.Terminal
|
||||
wantErr bool
|
||||
}{
|
||||
// {
|
||||
// desc: "empty container",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(ft)
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// return faketerm.MustNew(size)
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// desc: "container with a border",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(
|
||||
// ft,
|
||||
// Border(draw.LineStyleLight),
|
||||
// )
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// ft := faketerm.MustNew(size)
|
||||
// cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
// mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
// mustApply(cvs, ft)
|
||||
// return ft
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// desc: "horizontal split, children have borders",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(
|
||||
// ft,
|
||||
// SplitHorizontal(),
|
||||
// ).First(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root().Second(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root()
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// ft := faketerm.MustNew(size)
|
||||
// cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
// mustBox(cvs, image.Rect(0, 0, 10, 5), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(0, 5, 10, 10), draw.LineStyleLight)
|
||||
// mustApply(cvs, ft)
|
||||
// return ft
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// desc: "horizontal split, parent and children have borders",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(
|
||||
// ft,
|
||||
// SplitHorizontal(),
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).First(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root().Second(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root()
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// ft := faketerm.MustNew(size)
|
||||
// cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
// mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(1, 1, 9, 5), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(1, 5, 9, 9), draw.LineStyleLight)
|
||||
// mustApply(cvs, ft)
|
||||
// return ft
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// desc: "vertical split, children have borders",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(
|
||||
// ft,
|
||||
// SplitVertical(),
|
||||
// ).First(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root().Second(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root()
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// ft := faketerm.MustNew(size)
|
||||
// cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
// mustBox(cvs, image.Rect(0, 0, 5, 10), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(5, 0, 10, 10), draw.LineStyleLight)
|
||||
// mustApply(cvs, ft)
|
||||
// return ft
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// desc: "vertical split, parent and children have borders",
|
||||
// termSize: image.Point{10, 10},
|
||||
// container: func(ft *faketerm.Terminal) *Container {
|
||||
// return New(
|
||||
// ft,
|
||||
// SplitVertical(),
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).First(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root().Second(
|
||||
// Border(draw.LineStyleLight),
|
||||
// ).Root()
|
||||
// },
|
||||
// want: func(size image.Point) *faketerm.Terminal {
|
||||
// ft := faketerm.MustNew(size)
|
||||
// cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
// mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(1, 1, 5, 9), draw.LineStyleLight)
|
||||
// mustBox(cvs, image.Rect(5, 1, 9, 9), draw.LineStyleLight)
|
||||
// mustApply(cvs, ft)
|
||||
// return ft
|
||||
// },
|
||||
// },
|
||||
{
|
||||
desc: "empty container",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(ft)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
return faketerm.MustNew(size)
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "container with a border",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
Border(draw.LineStyleLight),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
mustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "horizontal split, children have borders",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
mustBox(cvs, image.Rect(0, 0, 10, 5), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(0, 5, 10, 10), draw.LineStyleLight)
|
||||
mustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "horizontal split, parent and children have borders",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
Border(draw.LineStyleLight),
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(1, 1, 9, 5), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(1, 5, 9, 9), draw.LineStyleLight)
|
||||
mustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "vertical split, children have borders",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
SplitVertical(
|
||||
Left(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Right(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
mustBox(cvs, image.Rect(0, 0, 5, 10), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(5, 0, 10, 10), draw.LineStyleLight)
|
||||
mustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "vertical split, parent and children have borders",
|
||||
termSize: image.Point{10, 10},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
Border(draw.LineStyleLight),
|
||||
SplitVertical(
|
||||
Left(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Right(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
cvs := mustCanvas(image.Rect(0, 0, 10, 10))
|
||||
mustBox(cvs, image.Rect(0, 0, 10, 10), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(1, 1, 5, 9), draw.LineStyleLight)
|
||||
mustBox(cvs, image.Rect(5, 1, 9, 9), draw.LineStyleLight)
|
||||
mustApply(cvs, ft)
|
||||
return ft
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "multi level split",
|
||||
termSize: image.Point{10, 11},
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
SplitVertical(),
|
||||
).First(
|
||||
SplitHorizontal(),
|
||||
).First(
|
||||
Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
SplitHorizontal(),
|
||||
).First(
|
||||
Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
Border(draw.LineStyleLight),
|
||||
).Root().Second(
|
||||
Border(draw.LineStyleLight),
|
||||
).Root()
|
||||
SplitVertical(
|
||||
Left(
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Right(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
@ -252,16 +252,22 @@ func TestDraw(t *testing.T) {
|
||||
container: func(ft *faketerm.Terminal) *Container {
|
||||
return New(
|
||||
ft,
|
||||
SplitHorizontal(),
|
||||
).First(
|
||||
Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
SplitHorizontal(),
|
||||
).First(
|
||||
Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
Border(draw.LineStyleLight),
|
||||
).Root()
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
SplitHorizontal(
|
||||
Top(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
Bottom(
|
||||
Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
want: func(size image.Point) *faketerm.Terminal {
|
||||
ft := faketerm.MustNew(size)
|
||||
|
@ -7,10 +7,17 @@ import (
|
||||
"github.com/mum4k/termdash/widget"
|
||||
)
|
||||
|
||||
// Option is used to provide options.
|
||||
// applyOptions applies the options to the container.
|
||||
func applyOptions(c *Container, opts ...Option) {
|
||||
for _, opt := range opts {
|
||||
opt.set(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Option is used to provide options to a container.
|
||||
type Option interface {
|
||||
// set sets the provided option.
|
||||
set(*options)
|
||||
set(*Container)
|
||||
}
|
||||
|
||||
// options stores the options provided to the container.
|
||||
@ -32,33 +39,45 @@ type options struct {
|
||||
}
|
||||
|
||||
// option implements Option.
|
||||
type option func(*options)
|
||||
type option func(*Container)
|
||||
|
||||
// set implements Option.set.
|
||||
func (o option) set(opts *options) {
|
||||
o(opts)
|
||||
func (o option) set(c *Container) {
|
||||
o(c)
|
||||
}
|
||||
|
||||
// SplitVertical splits the container along the vertical axis into two sub
|
||||
// containers. The use of this option removes any widget placed at this
|
||||
// container, containers with sub containers cannot contain widgets.
|
||||
func SplitVertical(l LeftOption, r RightOption) Option {
|
||||
return option(func(c *Container) {
|
||||
c.opts.split = splitTypeVertical
|
||||
c.opts.widget = nil
|
||||
applyOptions(c.createFirst(), l.lOpts()...)
|
||||
applyOptions(c.createSecond(), r.rOpts()...)
|
||||
})
|
||||
}
|
||||
|
||||
// SplitHorizontal splits the container along the horizontal axis into two sub
|
||||
// containers. The use of this option removes any widget placed at this
|
||||
// container, containers with sub containers cannot contain widgets.
|
||||
func SplitHorizontal(t TopOption, b BottomOption) Option {
|
||||
return option(func(c *Container) {
|
||||
c.opts.split = splitTypeHorizontal
|
||||
c.opts.widget = nil
|
||||
applyOptions(c.createFirst(), t.tOpts()...)
|
||||
applyOptions(c.createSecond(), b.bOpts()...)
|
||||
})
|
||||
}
|
||||
|
||||
// PlaceWidget places the provided widget into the container.
|
||||
// The use of this option removes any sub containers. Containers with sub
|
||||
// containers cannot have widgets.
|
||||
func PlaceWidget(w widget.Widget) Option {
|
||||
return option(func(opts *options) {
|
||||
opts.widget = w
|
||||
})
|
||||
}
|
||||
|
||||
// SplitHorizontal configures the container for a horizontal split.
|
||||
func SplitHorizontal() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.split = splitTypeHorizontal
|
||||
})
|
||||
}
|
||||
|
||||
// SplitVertical configures the container for a vertical split.
|
||||
// This is the default split type if neither if SplitHorizontal() or
|
||||
// SplitVertical() is specified.
|
||||
func SplitVertical() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.split = splitTypeVertical
|
||||
return option(func(c *Container) {
|
||||
c.opts.widget = w
|
||||
c.first = nil
|
||||
c.second = nil
|
||||
})
|
||||
}
|
||||
|
||||
@ -66,8 +85,8 @@ func SplitVertical() Option {
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget. This is the default horizontal alignment if no other is specified.
|
||||
func HorizontalAlignLeft() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.hAlign = hAlignTypeLeft
|
||||
return option(func(c *Container) {
|
||||
c.opts.hAlign = hAlignTypeLeft
|
||||
})
|
||||
}
|
||||
|
||||
@ -75,8 +94,8 @@ func HorizontalAlignLeft() Option {
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func HorizontalAlignCenter() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.hAlign = hAlignTypeCenter
|
||||
return option(func(c *Container) {
|
||||
c.opts.hAlign = hAlignTypeCenter
|
||||
})
|
||||
}
|
||||
|
||||
@ -84,8 +103,8 @@ func HorizontalAlignCenter() Option {
|
||||
// container along the horizontal axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func HorizontalAlignRight() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.hAlign = hAlignTypeRight
|
||||
return option(func(c *Container) {
|
||||
c.opts.hAlign = hAlignTypeRight
|
||||
})
|
||||
}
|
||||
|
||||
@ -93,8 +112,8 @@ func HorizontalAlignRight() Option {
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget. This is the default vertical alignment if no other is specified.
|
||||
func VerticalAlignTop() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.vAlign = vAlignTypeTop
|
||||
return option(func(c *Container) {
|
||||
c.opts.vAlign = vAlignTypeTop
|
||||
})
|
||||
}
|
||||
|
||||
@ -102,8 +121,8 @@ func VerticalAlignTop() Option {
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func VerticalAlignMiddle() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.vAlign = vAlignTypeMiddle
|
||||
return option(func(c *Container) {
|
||||
c.opts.vAlign = vAlignTypeMiddle
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,15 +130,15 @@ func VerticalAlignMiddle() Option {
|
||||
// container along the vertical axis. Has no effect if the container contains
|
||||
// no widget.
|
||||
func VerticalAlignBottom() Option {
|
||||
return option(func(opts *options) {
|
||||
opts.vAlign = vAlignTypeBottom
|
||||
return option(func(c *Container) {
|
||||
c.opts.vAlign = vAlignTypeBottom
|
||||
})
|
||||
}
|
||||
|
||||
// Border configures the container to have a border of the specified style.
|
||||
func Border(ls draw.LineStyle) Option {
|
||||
return option(func(opts *options) {
|
||||
opts.border = ls
|
||||
return option(func(c *Container) {
|
||||
c.opts.border = ls
|
||||
})
|
||||
}
|
||||
|
||||
@ -192,3 +211,103 @@ const (
|
||||
vAlignTypeMiddle
|
||||
vAlignTypeBottom
|
||||
)
|
||||
|
||||
// LeftOption is used to provide options to the left sub container after a
|
||||
// vertical split of the parent.
|
||||
type LeftOption interface {
|
||||
// lOpts returns the options.
|
||||
lOpts() []Option
|
||||
}
|
||||
|
||||
// leftOption implements LeftOption.
|
||||
type leftOption func() []Option
|
||||
|
||||
// lOpts implements LeftOption.lOpts.
|
||||
func (lo leftOption) lOpts() []Option {
|
||||
if lo == nil {
|
||||
return nil
|
||||
}
|
||||
return lo()
|
||||
}
|
||||
|
||||
// Left applies options to the left sub container after a vertical split of the parent.
|
||||
func Left(opts ...Option) LeftOption {
|
||||
return leftOption(func() []Option {
|
||||
return opts
|
||||
})
|
||||
}
|
||||
|
||||
// RightOption is used to provide options to the right sub container after a
|
||||
// vertical split of the parent.
|
||||
type RightOption interface {
|
||||
// rOpts returns the options.
|
||||
rOpts() []Option
|
||||
}
|
||||
|
||||
// rightOption implements RightOption.
|
||||
type rightOption func() []Option
|
||||
|
||||
// rOpts implements RightOption.rOpts.
|
||||
func (lo rightOption) rOpts() []Option {
|
||||
if lo == nil {
|
||||
return nil
|
||||
}
|
||||
return lo()
|
||||
}
|
||||
|
||||
// Right applies options to the right sub container after a vertical split of the parent.
|
||||
func Right(opts ...Option) RightOption {
|
||||
return rightOption(func() []Option {
|
||||
return opts
|
||||
})
|
||||
}
|
||||
|
||||
// TopOption is used to provide options to the top sub container after a
|
||||
// horizontal split of the parent.
|
||||
type TopOption interface {
|
||||
// tOpts returns the options.
|
||||
tOpts() []Option
|
||||
}
|
||||
|
||||
// topOption implements TopOption.
|
||||
type topOption func() []Option
|
||||
|
||||
// tOpts implements TopOption.tOpts.
|
||||
func (lo topOption) tOpts() []Option {
|
||||
if lo == nil {
|
||||
return nil
|
||||
}
|
||||
return lo()
|
||||
}
|
||||
|
||||
// Top applies options to the top sub container after a horizontal split of the parent.
|
||||
func Top(opts ...Option) TopOption {
|
||||
return topOption(func() []Option {
|
||||
return opts
|
||||
})
|
||||
}
|
||||
|
||||
// BottomOption is used to provide options to the bottom sub container after a
|
||||
// horizontal split of the parent.
|
||||
type BottomOption interface {
|
||||
// bOpts returns the options.
|
||||
bOpts() []Option
|
||||
}
|
||||
|
||||
// bottomOption implements BottomOption.
|
||||
type bottomOption func() []Option
|
||||
|
||||
// bOpts implements BottomOption.bOpts.
|
||||
func (lo bottomOption) bOpts() []Option {
|
||||
if lo == nil {
|
||||
return nil
|
||||
}
|
||||
return lo()
|
||||
}
|
||||
|
||||
// Bottom applies options to the bottom sub container after a horizontal split of the parent.
|
||||
func Bottom(opts ...Option) BottomOption {
|
||||
return bottomOption(func() []Option {
|
||||
return opts
|
||||
})
|
||||
}
|
||||
|
@ -18,20 +18,29 @@ func main() {
|
||||
|
||||
c := container.New(
|
||||
t,
|
||||
container.SplitVertical(),
|
||||
).First(
|
||||
container.SplitHorizontal(),
|
||||
).First(
|
||||
container.Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
container.SplitHorizontal(),
|
||||
).First(
|
||||
container.Border(draw.LineStyleLight),
|
||||
).Parent().Second(
|
||||
container.Border(draw.LineStyleLight),
|
||||
).Root().Second(
|
||||
container.Border(draw.LineStyleLight),
|
||||
).Root()
|
||||
container.SplitVertical(
|
||||
container.Left(
|
||||
container.SplitHorizontal(
|
||||
container.Top(
|
||||
container.Border(draw.LineStyleLight),
|
||||
),
|
||||
container.Bottom(
|
||||
container.SplitHorizontal(
|
||||
container.Top(
|
||||
container.Border(draw.LineStyleLight),
|
||||
),
|
||||
container.Bottom(
|
||||
container.Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
container.Right(
|
||||
container.Border(draw.LineStyleLight),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
if err := c.Draw(); err != nil {
|
||||
panic(err)
|
||||
@ -40,5 +49,5 @@ func main() {
|
||||
if err := t.Flush(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(30 * time.Second)
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user