156 lines
3.6 KiB
Go
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 >= 0 {
|
|
w++
|
|
} else if side.Vertical() && h >= 0 {
|
|
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: '║',
|
|
}
|
|
}
|