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 }
 |