termdash/container/options.go

321 lines
8.5 KiB
Go

// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package container
// options.go defines container options.
import (
"github.com/mum4k/termdash/align"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/draw"
"github.com/mum4k/termdash/widgetapi"
)
// 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(*Container)
}
// options stores the options provided to the container.
type options struct {
// inherited are options that are inherited by child containers.
inherited inherited
// split identifies how is this container split.
split splitType
// widget is the widget in the container.
// A container can have either two sub containers (left and right) or a
// widget. But not both.
widget widgetapi.Widget
// Alignment of the widget if present.
hAlign align.Horizontal
vAlign align.Vertical
// border is the border around the container.
border draw.LineStyle
borderTitle string
borderTitleHAlign align.Horizontal
}
// inherited contains options that are inherited by child containers.
type inherited struct {
// borderColor is the color used for the border.
borderColor cell.Color
// focusedColor is the color used for the border when focused.
focusedColor cell.Color
}
// newOptions returns a new options instance with the default values.
// Parent are the inherited options from the parent container or nil if these
// options are for a container with no parent (the root).
func newOptions(parent *options) *options {
opts := &options{
inherited: inherited{
focusedColor: cell.ColorYellow,
},
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
}
if parent != nil {
opts.inherited = parent.inherited
}
return opts
}
// option implements Option.
type option func(*Container)
// set implements Option.set.
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 widgetapi.Widget) Option {
return option(func(c *Container) {
c.opts.widget = w
c.first = nil
c.second = nil
})
}
// AlignHorizontal sets the horizontal alignment for the widget placed in the
// container. Has no effect if the container contains no widget.
// Defaults alignment in the center.
func AlignHorizontal(h align.Horizontal) Option {
return option(func(c *Container) {
c.opts.hAlign = h
})
}
// AlignVertical sets the vertical alignment for the widget placed in the container.
// Has no effect if the container contains no widget.
// Defaults to alignment in the middle.
func AlignVertical(v align.Vertical) Option {
return option(func(c *Container) {
c.opts.vAlign = v
})
}
// Border configures the container to have a border of the specified style.
func Border(ls draw.LineStyle) Option {
return option(func(c *Container) {
c.opts.border = ls
})
}
// BorderTitle sets a text title within the border.
func BorderTitle(title string) Option {
return option(func(c *Container) {
c.opts.borderTitle = title
})
}
// BorderTitleAlignLeft aligns the border title on the left.
func BorderTitleAlignLeft() Option {
return option(func(c *Container) {
c.opts.borderTitleHAlign = align.HorizontalLeft
})
}
// BorderTitleAlignCenter aligns the border title in the center.
func BorderTitleAlignCenter() Option {
return option(func(c *Container) {
c.opts.borderTitleHAlign = align.HorizontalCenter
})
}
// BorderTitleAlignRight aligns the border title on the right.
func BorderTitleAlignRight() Option {
return option(func(c *Container) {
c.opts.borderTitleHAlign = align.HorizontalRight
})
}
// BorderColor sets the color of the border around the container.
// This option is inherited to sub containers created by container splits.
func BorderColor(color cell.Color) Option {
return option(func(c *Container) {
c.opts.inherited.borderColor = color
})
}
// FocusedColor sets the color of the border around the container when it has
// keyboard focus.
// This option is inherited to sub containers created by container splits.
func FocusedColor(color cell.Color) Option {
return option(func(c *Container) {
c.opts.inherited.focusedColor = color
})
}
// splitType identifies how a container is split.
type splitType int
// String implements fmt.Stringer()
func (st splitType) String() string {
if n, ok := splitTypeNames[st]; ok {
return n
}
return "splitTypeUnknown"
}
// splitTypeNames maps splitType values to human readable names.
var splitTypeNames = map[splitType]string{
splitTypeVertical: "splitTypeVertical",
splitTypeHorizontal: "splitTypeHorizontal",
}
const (
splitTypeVertical splitType = iota
splitTypeHorizontal
)
// 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
})
}