termui/block.go

241 lines
5.2 KiB
Go
Raw Normal View History

2017-01-14 14:07:43 +08:00
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
2015-03-21 04:21:50 +08:00
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.
2015-02-04 03:13:51 +08:00
package termui
import "image"
2015-04-26 12:13:49 +08:00
// 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()
}
2015-05-03 11:35:06 +08:00
return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
2015-04-26 12:13:49 +08:00
}
// Buffer draws a vertical line.
func (l Vline) Buffer() Buffer {
if l.Len <= 0 {
return NewBuffer()
}
2015-05-03 11:35:06 +08:00
return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
2015-04-26 12:13:49 +08:00
}
// 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
2015-05-03 11:35:06 +08:00
x1 := max.X - 1
y1 := max.Y - 1
2015-04-26 12:13:49 +08:00
// 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
2015-05-10 07:29:22 +08:00
tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
2015-04-26 12:13:49 +08:00
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()
}
}
2015-03-25 05:16:43 +08:00
// 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.
2015-02-04 09:56:49 +08:00
type Block struct {
2015-04-26 12:13:49 +08:00
area image.Rectangle
innerArea image.Rectangle
2015-02-05 05:57:24 +08:00
X int
Y int
2015-04-26 12:13:49 +08:00
Border bool
BorderFg Attribute
BorderBg Attribute
BorderLeft bool
BorderRight bool
BorderTop bool
BorderBottom bool
BorderLabel string
BorderLabelFg Attribute
BorderLabelBg Attribute
Display bool
Bg Attribute
2015-02-05 05:57:24 +08:00
Width int
Height int
PaddingTop int
PaddingBottom int
PaddingLeft int
PaddingRight int
2015-10-08 02:25:59 +08:00
id string
2015-10-13 13:00:15 +08:00
Float Align
2015-02-04 03:13:51 +08:00
}
2015-03-25 05:16:43 +08:00
// NewBlock returns a *Block which inherits styles from current theme.
2015-02-04 09:56:49 +08:00
func NewBlock() *Block {
2015-04-26 12:13:49 +08:00
b := Block{}
b.Display = true
2015-09-21 15:11:58 +08:00
b.Border = true
2015-04-26 12:13:49 +08:00
b.BorderLeft = true
b.BorderRight = true
b.BorderTop = true
b.BorderBottom = true
2015-09-21 15:11:58 +08:00
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")
2015-04-26 12:13:49 +08:00
b.Width = 2
b.Height = 2
2015-10-08 02:25:59 +08:00
b.id = GenId()
2015-10-13 13:00:15 +08:00
b.Float = AlignNone
2015-04-26 12:13:49 +08:00
return &b
2015-02-04 03:13:51 +08:00
}
2015-10-08 02:25:59 +08:00
func (b Block) Id() string {
return b.id
}
2015-09-21 15:11:58 +08:00
// Align computes box model
2015-04-26 12:13:49 +08:00
func (b *Block) Align() {
2015-10-13 13:00:15 +08:00
// 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
2015-04-26 12:13:49 +08:00
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--
}
}
2015-02-04 03:13:51 +08:00
}
// InnerBounds returns the internal bounds of the block after aligning and
// calculating the padding and border, if any.
2015-04-26 12:13:49 +08:00
func (b *Block) InnerBounds() image.Rectangle {
b.Align()
return b.innerArea
}
2015-03-25 05:16:43 +08:00
// Buffer implements Bufferer interface.
// Draw background and border (if any).
2015-04-26 12:13:49 +08:00
func (b *Block) Buffer() Buffer {
b.Align()
2015-02-04 03:13:51 +08:00
buf := NewBuffer()
2015-04-26 12:13:49 +08:00
buf.SetArea(b.area)
buf.Fill(' ', ColorDefault, b.Bg)
2015-02-04 03:13:51 +08:00
2015-04-26 12:13:49 +08:00
b.drawBorder(buf)
b.drawBorderLabel(buf)
2015-02-04 03:13:51 +08:00
return buf
2015-02-04 03:13:51 +08:00
}
2015-03-16 03:56:38 +08:00
2015-03-25 05:16:43 +08:00
// GetHeight implements GridBufferer.
// It returns current height of the block.
2015-04-26 12:13:49 +08:00
func (b Block) GetHeight() int {
return b.Height
2015-03-16 03:56:38 +08:00
}
2015-03-25 05:16:43 +08:00
// SetX implements GridBufferer interface, which sets block's x position.
2015-04-26 12:13:49 +08:00
func (b *Block) SetX(x int) {
b.X = x
2015-03-16 03:56:38 +08:00
}
2015-03-25 05:16:43 +08:00
// SetY implements GridBufferer interface, it sets y position for block.
2015-04-26 12:13:49 +08:00
func (b *Block) SetY(y int) {
b.Y = y
2015-03-16 03:56:38 +08:00
}
2015-03-25 05:16:43 +08:00
// SetWidth implements GridBuffer interface, it sets block's width.
2015-04-26 12:13:49 +08:00
func (b *Block) SetWidth(w int) {
b.Width = w
}
2015-10-21 09:45:28 +08:00
func (b Block) InnerWidth() int {
2015-10-22 23:17:20 +08:00
return b.innerArea.Dx()
2015-10-21 09:45:28 +08:00
}
func (b Block) InnerHeight() int {
2015-10-22 23:17:20 +08:00
return b.innerArea.Dy()
2015-10-21 09:45:28 +08:00
}
func (b Block) InnerX() int {
2015-10-22 23:17:20 +08:00
return b.innerArea.Min.X
2015-10-21 09:45:28 +08:00
}
2015-10-22 23:17:20 +08:00
func (b Block) InnerY() int { return b.innerArea.Min.Y }