2015-03-21 04:21:50 +08:00
|
|
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
|
|
|
// 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
|
|
|
|
|
2015-04-21 21:56:10 +08:00
|
|
|
import "image"
|
|
|
|
|
2015-04-26 12:13:49 +08:00
|
|
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
|
|
|
// Use of this source code is governed by a MIT license that can
|
|
|
|
// be found in the LICENSE file.
|
|
|
|
|
|
|
|
// 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, 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, 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
|
|
|
|
y1 := max.Y
|
|
|
|
|
|
|
|
// 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(TextCells(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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
2015-04-21 21:56:10 +08:00
|
|
|
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
|
2015-04-21 21:56:10 +08:00
|
|
|
Bg Attribute
|
2015-02-05 05:57:24 +08:00
|
|
|
Width int
|
|
|
|
Height int
|
|
|
|
PaddingTop int
|
|
|
|
PaddingBottom int
|
|
|
|
PaddingLeft int
|
|
|
|
PaddingRight int
|
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
|
|
|
|
b.Border = theme.HasBorder
|
|
|
|
b.BorderLeft = true
|
|
|
|
b.BorderRight = true
|
|
|
|
b.BorderTop = true
|
|
|
|
b.BorderBottom = true
|
|
|
|
b.BorderBg = theme.BorderBg
|
|
|
|
b.BorderFg = theme.BorderFg
|
|
|
|
b.BorderLabelBg = theme.BorderLabelTextBg
|
|
|
|
b.BorderLabelFg = theme.BorderLabelTextFg
|
|
|
|
b.Bg = theme.BlockBg
|
|
|
|
b.Width = 2
|
|
|
|
b.Height = 2
|
|
|
|
return &b
|
2015-02-04 03:13:51 +08:00
|
|
|
}
|
|
|
|
|
2015-04-26 12:13:49 +08:00
|
|
|
// Align computes box mob.l
|
|
|
|
func (b *Block) Align() {
|
|
|
|
b.area.Min.X = b.X
|
|
|
|
b.area.Min.Y = b.Y
|
|
|
|
b.area.Max.X = b.X + b.Width - 1
|
|
|
|
b.area.Max.Y = b.Y + b.Height - 1
|
|
|
|
|
|
|
|
b.innerArea.Min.X = b.X + b.PaddingLeft
|
|
|
|
b.innerArea.Min.Y = b.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--
|
2015-04-21 21:56:10 +08:00
|
|
|
}
|
|
|
|
}
|
2015-02-04 03:13:51 +08:00
|
|
|
}
|
|
|
|
|
2015-04-09 10:19:43 +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-04-09 10:19:43 +08:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2015-04-21 21:56:10 +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
|
|
|
|
2015-04-21 21:56:10 +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-03-16 03:56:38 +08:00
|
|
|
}
|