tui/views/view_border.go
2023-04-24 11:55:04 +02:00

156 lines
3.6 KiB
Go

package views
import "git.milar.in/milarin/tui"
// BorderView is a tui.Wrapper which draws an ASCII border around its view.
// Be aware that box drawing characters must share the same tui.Style with the next character.
// This can lead to color artifacts when using BorderView with colored styles.
type BorderView struct {
tui.WrapperTmpl
Border BorderBox
ShowBorder map[tui.Side]bool
}
var _ tui.Wrapper = &BorderView{}
// NewBorderView create a new BorderView which show borders on the given sides around its view.
// If no sides are given, all sides will have a border by default
func NewBorderView(view tui.View, showBorders ...tui.Side) *BorderView {
v := new(BorderView)
v.SetView(view)
v.Border = ThinBorder()
if len(showBorders) == 0 {
v.ShowBorder = map[tui.Side]bool{
tui.SideBottom: true,
tui.SideTop: true,
tui.SideLeft: true,
tui.SideRight: true,
}
} else {
v.ShowBorder = map[tui.Side]bool{}
for _, border := range showBorders {
v.ShowBorder[border] = true
}
}
return v
}
func (g *BorderView) Draw(buf *tui.ViewBuffer) {
viewbuf := buf
// limit view buffer for child view
if g.ShowBorder[tui.SideTop] {
viewbuf = viewbuf.Sub(0, 1, viewbuf.Width(), viewbuf.Height())
}
if g.ShowBorder[tui.SideBottom] {
viewbuf = viewbuf.Sub(0, 0, viewbuf.Width(), viewbuf.Height()-1)
}
if g.ShowBorder[tui.SideLeft] {
viewbuf = viewbuf.Sub(1, 0, viewbuf.Width(), viewbuf.Height())
}
if g.ShowBorder[tui.SideRight] {
viewbuf = viewbuf.Sub(0, 0, viewbuf.Width()-1, viewbuf.Height())
}
// draw child view
g.View().Draw(viewbuf)
// draw horizontal lines
for x := 0; x < buf.Width(); x++ {
if g.ShowBorder[tui.SideTop] {
buf.Set(x, 0, tui.Rune{Rn: g.Border.Horizontal, Style: g.Style()})
}
if g.ShowBorder[tui.SideBottom] {
buf.Set(x, buf.Height()-1, tui.Rune{Rn: g.Border.Horizontal, Style: g.Style()})
}
}
// draw vertical lines
for y := 0; y < buf.Height(); y++ {
if g.ShowBorder[tui.SideLeft] {
buf.Set(0, y, tui.Rune{Rn: g.Border.Vertical, Style: g.Style()})
}
if g.ShowBorder[tui.SideRight] {
buf.Set(buf.Width()-1, y, tui.Rune{Rn: g.Border.Vertical, Style: g.Style()})
}
}
// draw corners
if g.ShowBorder[tui.SideTop] {
if g.ShowBorder[tui.SideLeft] {
buf.Set(0, 0, tui.Rune{Rn: g.Border.TopLeft, Style: g.Style()})
}
if g.ShowBorder[tui.SideRight] {
buf.Set(buf.Width()-1, 0, tui.Rune{Rn: g.Border.TopRight, Style: g.Style()})
}
}
if g.ShowBorder[tui.SideBottom] {
if g.ShowBorder[tui.SideLeft] {
buf.Set(0, buf.Height()-1, tui.Rune{Rn: g.Border.BottomLeft, Style: g.Style()})
}
if g.ShowBorder[tui.SideRight] {
buf.Set(buf.Width()-1, buf.Height()-1, tui.Rune{Rn: g.Border.BottomRight, Style: g.Style()})
}
}
}
func (v *BorderView) Layout() (prefWidth, prefHeight int) {
w, h := v.View().Layout()
for side, border := range v.ShowBorder {
if border {
if side.Horizontal() {
w++
} else if side.Vertical() {
h++
}
}
}
return w, h
}
type BorderBox struct {
TopLeft rune
TopRight rune
BottomLeft rune
BottomRight rune
Horizontal rune
Vertical rune
}
func ThickBorder() BorderBox {
return BorderBox{
TopLeft: '┏',
TopRight: '┓',
BottomLeft: '┗',
BottomRight: '┛',
Horizontal: '━',
Vertical: '┃',
}
}
func ThinBorder() BorderBox {
return BorderBox{
TopLeft: '┌',
TopRight: '┐',
BottomLeft: '└',
BottomRight: '┘',
Horizontal: '─',
Vertical: '│',
}
}
func DoubleBorder() BorderBox {
return BorderBox{
TopLeft: '╔',
TopRight: '╗',
BottomLeft: '╚',
BottomRight: '╝',
Horizontal: '═',
Vertical: '║',
}
}