241 lines
5.2 KiB
Go
241 lines
5.2 KiB
Go
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
|
|
// Use of this source code is governed by a MIT license that can
|
|
// be found in the LICENSE file.
|
|
|
|
package termui
|
|
|
|
import "image"
|
|
|
|
// Hline is a horizontal line.
|
|
type Hline struct {
|
|
X int
|
|
Y int
|
|
Len int
|
|
Fg Attribute
|
|
Bg Attribute
|
|
}
|
|
|
|
// Vline is a vertical line.
|
|
type Vline struct {
|
|
X int
|
|
Y int
|
|
Len int
|
|
Fg Attribute
|
|
Bg Attribute
|
|
}
|
|
|
|
// Buffer draws a horizontal line.
|
|
func (l Hline) Buffer() Buffer {
|
|
if l.Len <= 0 {
|
|
return NewBuffer()
|
|
}
|
|
return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
|
|
}
|
|
|
|
// Buffer draws a vertical line.
|
|
func (l Vline) Buffer() Buffer {
|
|
if l.Len <= 0 {
|
|
return NewBuffer()
|
|
}
|
|
return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
|
|
}
|
|
|
|
// Buffer draws a box border.
|
|
func (b Block) drawBorder(buf Buffer) {
|
|
if !b.Border {
|
|
return
|
|
}
|
|
|
|
min := b.area.Min
|
|
max := b.area.Max
|
|
|
|
x0 := min.X
|
|
y0 := min.Y
|
|
x1 := max.X - 1
|
|
y1 := max.Y - 1
|
|
|
|
// draw lines
|
|
if b.BorderTop {
|
|
buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
|
|
}
|
|
if b.BorderBottom {
|
|
buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
|
|
}
|
|
if b.BorderLeft {
|
|
buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
|
|
}
|
|
if b.BorderRight {
|
|
buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
|
|
}
|
|
|
|
// draw corners
|
|
if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 {
|
|
buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg})
|
|
}
|
|
if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 {
|
|
buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg})
|
|
}
|
|
if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 {
|
|
buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg})
|
|
}
|
|
if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 {
|
|
buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg})
|
|
}
|
|
}
|
|
|
|
func (b Block) drawBorderLabel(buf Buffer) {
|
|
maxTxtW := b.area.Dx() - 2
|
|
tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
|
|
|
|
for i, w := 0, 0; i < len(tx); i++ {
|
|
buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
|
|
w += tx[i].Width()
|
|
}
|
|
}
|
|
|
|
// Block is a base struct for all other upper level widgets,
|
|
// consider it as css: display:block.
|
|
// Normally you do not need to create it manually.
|
|
type Block struct {
|
|
area image.Rectangle
|
|
innerArea image.Rectangle
|
|
X int
|
|
Y int
|
|
Border bool
|
|
BorderFg Attribute
|
|
BorderBg Attribute
|
|
BorderLeft bool
|
|
BorderRight bool
|
|
BorderTop bool
|
|
BorderBottom bool
|
|
BorderLabel string
|
|
BorderLabelFg Attribute
|
|
BorderLabelBg Attribute
|
|
Display bool
|
|
Bg Attribute
|
|
Width int
|
|
Height int
|
|
PaddingTop int
|
|
PaddingBottom int
|
|
PaddingLeft int
|
|
PaddingRight int
|
|
id string
|
|
Float Align
|
|
}
|
|
|
|
// NewBlock returns a *Block which inherits styles from current theme.
|
|
func NewBlock() *Block {
|
|
b := Block{}
|
|
b.Display = true
|
|
b.Border = true
|
|
b.BorderLeft = true
|
|
b.BorderRight = true
|
|
b.BorderTop = true
|
|
b.BorderBottom = true
|
|
b.BorderBg = ThemeAttr("border.bg")
|
|
b.BorderFg = ThemeAttr("border.fg")
|
|
b.BorderLabelBg = ThemeAttr("label.bg")
|
|
b.BorderLabelFg = ThemeAttr("label.fg")
|
|
b.Bg = ThemeAttr("block.bg")
|
|
b.Width = 2
|
|
b.Height = 2
|
|
b.id = GenId()
|
|
b.Float = AlignNone
|
|
return &b
|
|
}
|
|
|
|
func (b Block) Id() string {
|
|
return b.id
|
|
}
|
|
|
|
// Align computes box model
|
|
func (b *Block) Align() {
|
|
// outer
|
|
b.area.Min.X = 0
|
|
b.area.Min.Y = 0
|
|
b.area.Max.X = b.Width
|
|
b.area.Max.Y = b.Height
|
|
|
|
// float
|
|
b.area = AlignArea(TermRect(), b.area, b.Float)
|
|
b.area = MoveArea(b.area, b.X, b.Y)
|
|
|
|
// inner
|
|
b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft
|
|
b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop
|
|
b.innerArea.Max.X = b.area.Max.X - b.PaddingRight
|
|
b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom
|
|
|
|
if b.Border {
|
|
if b.BorderLeft {
|
|
b.innerArea.Min.X++
|
|
}
|
|
if b.BorderRight {
|
|
b.innerArea.Max.X--
|
|
}
|
|
if b.BorderTop {
|
|
b.innerArea.Min.Y++
|
|
}
|
|
if b.BorderBottom {
|
|
b.innerArea.Max.Y--
|
|
}
|
|
}
|
|
}
|
|
|
|
// InnerBounds returns the internal bounds of the block after aligning and
|
|
// calculating the padding and border, if any.
|
|
func (b *Block) InnerBounds() image.Rectangle {
|
|
b.Align()
|
|
return b.innerArea
|
|
}
|
|
|
|
// Buffer implements Bufferer interface.
|
|
// Draw background and border (if any).
|
|
func (b *Block) Buffer() Buffer {
|
|
b.Align()
|
|
|
|
buf := NewBuffer()
|
|
buf.SetArea(b.area)
|
|
buf.Fill(' ', ColorDefault, b.Bg)
|
|
|
|
b.drawBorder(buf)
|
|
b.drawBorderLabel(buf)
|
|
|
|
return buf
|
|
}
|
|
|
|
// GetHeight implements GridBufferer.
|
|
// It returns current height of the block.
|
|
func (b Block) GetHeight() int {
|
|
return b.Height
|
|
}
|
|
|
|
// SetX implements GridBufferer interface, which sets block's x position.
|
|
func (b *Block) SetX(x int) {
|
|
b.X = x
|
|
}
|
|
|
|
// SetY implements GridBufferer interface, it sets y position for block.
|
|
func (b *Block) SetY(y int) {
|
|
b.Y = y
|
|
}
|
|
|
|
// SetWidth implements GridBuffer interface, it sets block's width.
|
|
func (b *Block) SetWidth(w int) {
|
|
b.Width = w
|
|
}
|
|
|
|
func (b Block) InnerWidth() int {
|
|
return b.innerArea.Dx()
|
|
}
|
|
|
|
func (b Block) InnerHeight() int {
|
|
return b.innerArea.Dy()
|
|
}
|
|
|
|
func (b Block) InnerX() int {
|
|
return b.innerArea.Min.X
|
|
}
|
|
|
|
func (b Block) InnerY() int { return b.innerArea.Min.Y }
|