WIP use Buffer instead of []Point in Bufferer
Merge https://github.com/Matt3o12/termui.git colored-list Merge https://github.com/funkygao/termui.git master Add subdir widget Use image Rectangle represent buffer area
This commit is contained in:
parent
0a29dad7e7
commit
7aed750f64
124
block.go
124
block.go
@ -4,22 +4,22 @@
|
|||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
|
||||||
// Block is a base struct for all other upper level widgets,
|
// Block is a base struct for all other upper level widgets,
|
||||||
// consider it as css: display:block.
|
// consider it as css: display:block.
|
||||||
// Normally you do not need to create it manually.
|
// Normally you do not need to create it manually.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
|
Area image.Rectangle
|
||||||
|
innerArea image.Rectangle
|
||||||
X int
|
X int
|
||||||
Y int
|
Y int
|
||||||
Border labeledBorder
|
Border LabeledBorder
|
||||||
IsDisplay bool
|
IsDisplay bool
|
||||||
HasBorder bool
|
HasBorder bool
|
||||||
BgColor Attribute
|
Bg Attribute
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
innerWidth int
|
|
||||||
innerHeight int
|
|
||||||
innerX int
|
|
||||||
innerY int
|
|
||||||
PaddingTop int
|
PaddingTop int
|
||||||
PaddingBottom int
|
PaddingBottom int
|
||||||
PaddingLeft int
|
PaddingLeft int
|
||||||
@ -31,75 +31,81 @@ func NewBlock() *Block {
|
|||||||
d := Block{}
|
d := Block{}
|
||||||
d.IsDisplay = true
|
d.IsDisplay = true
|
||||||
d.HasBorder = theme.HasBorder
|
d.HasBorder = theme.HasBorder
|
||||||
d.Border.BgColor = theme.BorderBg
|
d.Border.Left = true
|
||||||
d.Border.FgColor = theme.BorderFg
|
d.Border.Right = true
|
||||||
d.Border.LabelBgColor = theme.BorderLabelTextBg
|
d.Border.Top = true
|
||||||
d.Border.LabelFgColor = theme.BorderLabelTextFg
|
d.Border.Bottom = true
|
||||||
d.BgColor = theme.BlockBg
|
d.Border.Bg = theme.BorderBg
|
||||||
|
d.Border.Fg = theme.BorderFg
|
||||||
|
d.Border.LabelBgClr = theme.BorderLabelTextBg
|
||||||
|
d.Border.LabelFgClr = theme.BorderLabelTextFg
|
||||||
|
d.Bg = theme.BlockBg
|
||||||
d.Width = 2
|
d.Width = 2
|
||||||
d.Height = 2
|
d.Height = 2
|
||||||
return &d
|
return &d
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute box model
|
// Align computes box model
|
||||||
func (d *Block) align() {
|
func (d *Block) Align() {
|
||||||
d.innerWidth = d.Width - d.PaddingLeft - d.PaddingRight
|
d.Area.Min.X = d.X
|
||||||
d.innerHeight = d.Height - d.PaddingTop - d.PaddingBottom
|
d.Area.Min.Y = d.Y
|
||||||
d.innerX = d.X + d.PaddingLeft
|
d.Area.Max.X = d.X + d.Width - 1
|
||||||
d.innerY = d.Y + d.PaddingTop
|
d.Area.Max.Y = d.Y + d.Height - 1
|
||||||
|
|
||||||
|
d.innerArea.Min.X = d.X + d.PaddingLeft
|
||||||
|
d.innerArea.Min.Y = d.Y + d.PaddingTop
|
||||||
|
d.innerArea.Max.X = d.Area.Max.X - d.PaddingRight
|
||||||
|
d.innerArea.Max.Y = d.Area.Max.Y - d.PaddingBottom
|
||||||
|
|
||||||
|
d.Border.Area = d.Area
|
||||||
|
|
||||||
if d.HasBorder {
|
if d.HasBorder {
|
||||||
d.innerHeight -= 2
|
switch {
|
||||||
d.innerWidth -= 2
|
case d.Border.Left:
|
||||||
d.Border.X = d.X
|
d.innerArea.Min.X++
|
||||||
d.Border.Y = d.Y
|
fallthrough
|
||||||
d.Border.Width = d.Width
|
case d.Border.Right:
|
||||||
d.Border.Height = d.Height
|
d.innerArea.Max.X--
|
||||||
d.innerX++
|
fallthrough
|
||||||
d.innerY++
|
case d.Border.Top:
|
||||||
|
d.innerArea.Min.Y++
|
||||||
|
fallthrough
|
||||||
|
case d.Border.Bottom:
|
||||||
|
d.innerArea.Max.Y--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.innerHeight < 0 {
|
|
||||||
d.innerHeight = 0
|
|
||||||
}
|
|
||||||
if d.innerWidth < 0 {
|
|
||||||
d.innerWidth = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InnerBounds returns the internal bounds of the block after aligning and
|
// InnerBounds returns the internal bounds of the block after aligning and
|
||||||
// calculating the padding and border, if any.
|
// calculating the padding and border, if any.
|
||||||
func (d *Block) InnerBounds() (x, y, width, height int) {
|
func (d *Block) InnerBounds() image.Rectangle {
|
||||||
d.align()
|
d.Align()
|
||||||
return d.innerX, d.innerY, d.innerWidth, d.innerHeight
|
return d.innerArea
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer implements Bufferer interface.
|
// Buffer implements Bufferer interface.
|
||||||
// Draw background and border (if any).
|
// Draw background and border (if any).
|
||||||
func (d *Block) Buffer() []Point {
|
func (d *Block) Buffer() Buffer {
|
||||||
d.align()
|
d.Align()
|
||||||
|
|
||||||
ps := []Point{}
|
buf := NewBuffer()
|
||||||
|
buf.Area = d.Area
|
||||||
if !d.IsDisplay {
|
if !d.IsDisplay {
|
||||||
return ps
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render border
|
||||||
if d.HasBorder {
|
if d.HasBorder {
|
||||||
ps = d.Border.Buffer()
|
buf.Union(d.Border.Buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < d.innerWidth; i++ {
|
// render background
|
||||||
for j := 0; j < d.innerHeight; j++ {
|
for p := range buf.CellMap {
|
||||||
p := Point{}
|
if p.In(d.innerArea) {
|
||||||
p.X = d.X + 1 + i
|
buf.CellMap[p] = Cell{' ', ColorDefault, d.Bg}
|
||||||
p.Y = d.Y + 1 + j
|
|
||||||
p.Ch = ' '
|
|
||||||
p.Bg = d.BgColor
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ps
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeight implements GridBufferer.
|
// GetHeight implements GridBufferer.
|
||||||
@ -122,21 +128,3 @@ func (d *Block) SetY(y int) {
|
|||||||
func (d *Block) SetWidth(w int) {
|
func (d *Block) SetWidth(w int) {
|
||||||
d.Width = w
|
d.Width = w
|
||||||
}
|
}
|
||||||
|
|
||||||
// chop the overflow parts
|
|
||||||
func (d *Block) chopOverflow(ps []Point) []Point {
|
|
||||||
nps := make([]Point, 0, len(ps))
|
|
||||||
x := d.X
|
|
||||||
y := d.Y
|
|
||||||
w := d.Width
|
|
||||||
h := d.Height
|
|
||||||
for _, v := range ps {
|
|
||||||
if v.X >= x &&
|
|
||||||
v.X < x+w &&
|
|
||||||
v.Y >= y &&
|
|
||||||
v.Y < y+h {
|
|
||||||
nps = append(nps, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nps
|
|
||||||
}
|
|
||||||
|
198
box.go
198
box.go
@ -4,114 +4,120 @@
|
|||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
type border struct {
|
import "image"
|
||||||
X int
|
|
||||||
Y int
|
type Border struct {
|
||||||
Width int
|
Area image.Rectangle
|
||||||
Height int
|
Left bool
|
||||||
FgColor Attribute
|
Top bool
|
||||||
BgColor Attribute
|
Right bool
|
||||||
|
Bottom bool
|
||||||
|
Fg Attribute
|
||||||
|
Bg Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
type hline struct {
|
type Hline struct {
|
||||||
X int
|
X int
|
||||||
Y int
|
Y int
|
||||||
Length int
|
Len int
|
||||||
FgColor Attribute
|
Fg Attribute
|
||||||
BgColor Attribute
|
Bg Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
type vline struct {
|
type Vline struct {
|
||||||
X int
|
X int
|
||||||
Y int
|
Y int
|
||||||
Length int
|
Len int
|
||||||
FgColor Attribute
|
Fg Attribute
|
||||||
BgColor Attribute
|
Bg Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a horizontal line.
|
// Buffer draws a horizontal line.
|
||||||
func (l hline) Buffer() []Point {
|
func (l Hline) Buffer() Buffer {
|
||||||
pts := make([]Point, l.Length)
|
buf := NewBuffer()
|
||||||
for i := 0; i < l.Length; i++ {
|
for i := 0; i < l.Len; i++ {
|
||||||
pts[i].X = l.X + i
|
buf.Set(l.X+i, l.Y, Cell{HORIZONTAL_LINE, l.Fg, l.Bg})
|
||||||
pts[i].Y = l.Y
|
|
||||||
pts[i].Ch = HORIZONTAL_LINE
|
|
||||||
pts[i].Bg = l.BgColor
|
|
||||||
pts[i].Fg = l.FgColor
|
|
||||||
}
|
}
|
||||||
return pts
|
buf.Align()
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a vertical line.
|
// Buffer draws a vertical line.
|
||||||
func (l vline) Buffer() []Point {
|
func (l Vline) Buffer() Buffer {
|
||||||
pts := make([]Point, l.Length)
|
buf := NewBuffer()
|
||||||
for i := 0; i < l.Length; i++ {
|
for i := 0; i < l.Len; i++ {
|
||||||
pts[i].X = l.X
|
buf.Set(l.X, l.Y+i, Cell{VERTICAL_LINE, l.Fg, l.Bg})
|
||||||
pts[i].Y = l.Y + i
|
|
||||||
pts[i].Ch = VERTICAL_LINE
|
|
||||||
pts[i].Bg = l.BgColor
|
|
||||||
pts[i].Fg = l.FgColor
|
|
||||||
}
|
}
|
||||||
return pts
|
buf.Align()
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw a box border.
|
// Buffer draws a box border.
|
||||||
func (b border) Buffer() []Point {
|
func (b Border) Buffer() Buffer {
|
||||||
if b.Width < 2 || b.Height < 2 {
|
buf := NewBuffer()
|
||||||
return nil
|
if b.Area.Size().X < 2 || b.Area.Size().Y < 2 {
|
||||||
}
|
return buf
|
||||||
pts := make([]Point, 2*b.Width+2*b.Height-4)
|
|
||||||
|
|
||||||
pts[0].X = b.X
|
|
||||||
pts[0].Y = b.Y
|
|
||||||
pts[0].Fg = b.FgColor
|
|
||||||
pts[0].Bg = b.BgColor
|
|
||||||
pts[0].Ch = TOP_LEFT
|
|
||||||
|
|
||||||
pts[1].X = b.X + b.Width - 1
|
|
||||||
pts[1].Y = b.Y
|
|
||||||
pts[1].Fg = b.FgColor
|
|
||||||
pts[1].Bg = b.BgColor
|
|
||||||
pts[1].Ch = TOP_RIGHT
|
|
||||||
|
|
||||||
pts[2].X = b.X
|
|
||||||
pts[2].Y = b.Y + b.Height - 1
|
|
||||||
pts[2].Fg = b.FgColor
|
|
||||||
pts[2].Bg = b.BgColor
|
|
||||||
pts[2].Ch = BOTTOM_LEFT
|
|
||||||
|
|
||||||
pts[3].X = b.X + b.Width - 1
|
|
||||||
pts[3].Y = b.Y + b.Height - 1
|
|
||||||
pts[3].Fg = b.FgColor
|
|
||||||
pts[3].Bg = b.BgColor
|
|
||||||
pts[3].Ch = BOTTOM_RIGHT
|
|
||||||
|
|
||||||
copy(pts[4:], (hline{b.X + 1, b.Y, b.Width - 2, b.FgColor, b.BgColor}).Buffer())
|
|
||||||
copy(pts[4+b.Width-2:], (hline{b.X + 1, b.Y + b.Height - 1, b.Width - 2, b.FgColor, b.BgColor}).Buffer())
|
|
||||||
copy(pts[4+2*b.Width-4:], (vline{b.X, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer())
|
|
||||||
copy(pts[4+2*b.Width-4+b.Height-2:], (vline{b.X + b.Width - 1, b.Y + 1, b.Height - 2, b.FgColor, b.BgColor}).Buffer())
|
|
||||||
|
|
||||||
return pts
|
|
||||||
}
|
|
||||||
|
|
||||||
type labeledBorder struct {
|
|
||||||
border
|
|
||||||
Label string
|
|
||||||
LabelFgColor Attribute
|
|
||||||
LabelBgColor Attribute
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw a box border with label.
|
|
||||||
func (lb labeledBorder) Buffer() []Point {
|
|
||||||
ps := lb.border.Buffer()
|
|
||||||
maxTxtW := lb.Width - 2
|
|
||||||
rs := trimStr2Runes(lb.Label, maxTxtW)
|
|
||||||
|
|
||||||
for i, j, w := 0, 0, 0; i < len(rs); i++ {
|
|
||||||
w = charWidth(rs[i])
|
|
||||||
ps = append(ps, newPointWithAttrs(rs[i], lb.X+1+j, lb.Y, lb.LabelFgColor, lb.LabelBgColor))
|
|
||||||
j += w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ps
|
min := b.Area.Min
|
||||||
|
max := b.Area.Max
|
||||||
|
|
||||||
|
x0 := min.X
|
||||||
|
y0 := min.Y
|
||||||
|
x1 := max.X
|
||||||
|
y1 := max.Y
|
||||||
|
|
||||||
|
// draw lines
|
||||||
|
switch {
|
||||||
|
case b.Top:
|
||||||
|
buf.Union(Hline{x0, y0, x1 - x0, b.Fg, b.Bg}.Buffer())
|
||||||
|
fallthrough
|
||||||
|
case b.Bottom:
|
||||||
|
buf.Union(Hline{x0, y1, x1 - x0, b.Fg, b.Bg}.Buffer())
|
||||||
|
fallthrough
|
||||||
|
case b.Left:
|
||||||
|
buf.Union(Vline{x0, y0, y1 - y0, b.Fg, b.Bg}.Buffer())
|
||||||
|
fallthrough
|
||||||
|
case b.Right:
|
||||||
|
buf.Union(Vline{x1, y0, y1 - y0, b.Fg, b.Bg}.Buffer())
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw corners
|
||||||
|
switch {
|
||||||
|
case b.Top && b.Left:
|
||||||
|
buf.Set(x0, y0, Cell{TOP_LEFT, b.Fg, b.Bg})
|
||||||
|
fallthrough
|
||||||
|
case b.Top && b.Right:
|
||||||
|
buf.Set(x1, y0, Cell{TOP_RIGHT, b.Fg, b.Bg})
|
||||||
|
fallthrough
|
||||||
|
case b.Bottom && b.Left:
|
||||||
|
buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.Fg, b.Bg})
|
||||||
|
fallthrough
|
||||||
|
case b.Bottom && b.Right:
|
||||||
|
buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.Fg, b.Bg})
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabeledBorder defined label upon Border
|
||||||
|
type LabeledBorder struct {
|
||||||
|
Border
|
||||||
|
Label string
|
||||||
|
LabelFgClr Attribute
|
||||||
|
LabelBgClr Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer draw a box border with label.
|
||||||
|
func (lb LabeledBorder) Buffer() Buffer {
|
||||||
|
border := lb.Border.Buffer()
|
||||||
|
maxTxtW := lb.Area.Dx() + 1 - 2
|
||||||
|
tx := DTrimTxCls(TextCells(lb.Label, lb.LabelFgClr, lb.LabelBgClr), maxTxtW)
|
||||||
|
|
||||||
|
for i, w := 0, 0; i < len(tx); i++ {
|
||||||
|
border.Set(border.Area.Min.X+1+w, border.Area.Min.Y, tx[i])
|
||||||
|
w += tx[i].Width()
|
||||||
|
}
|
||||||
|
|
||||||
|
return border
|
||||||
}
|
}
|
||||||
|
89
buffer.go
Normal file
89
buffer.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package termui
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
|
||||||
|
// Cell is a rune with assigned Fg and Bg
|
||||||
|
type Cell struct {
|
||||||
|
Ch rune
|
||||||
|
Fg Attribute
|
||||||
|
Bg Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer is a renderable rectangle cell data container.
|
||||||
|
type Buffer struct {
|
||||||
|
Area image.Rectangle // selected drawing area
|
||||||
|
CellMap map[image.Point]Cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the cell at (x,y).
|
||||||
|
func (b Buffer) At(x, y int) Cell {
|
||||||
|
return b.CellMap[image.Pt(x, y)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set assigns a char to (x,y)
|
||||||
|
func (b Buffer) Set(x, y int, c Cell) {
|
||||||
|
b.CellMap[image.Pt(x, y)] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bounds returns the domain for which At can return non-zero color.
|
||||||
|
func (b Buffer) Bounds() image.Rectangle {
|
||||||
|
x0, y0, x1, y1 := 0, 0, 0, 0
|
||||||
|
for p := range b.CellMap {
|
||||||
|
switch {
|
||||||
|
case p.X > x1:
|
||||||
|
x1 = p.X
|
||||||
|
case p.X < x0:
|
||||||
|
x0 = p.X
|
||||||
|
case p.Y > y1:
|
||||||
|
y1 = p.Y
|
||||||
|
case p.Y < y0:
|
||||||
|
y0 = p.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image.Rect(x0, y0, x1, y1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align sets drawing area to the buffer's bound
|
||||||
|
func (b *Buffer) Align() {
|
||||||
|
b.Area = b.Bounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCell returns a new cell
|
||||||
|
func NewCell(ch rune, fg, bg Attribute) Cell {
|
||||||
|
return Cell{ch, fg, bg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union squeezes buf into b
|
||||||
|
func (b Buffer) Union(buf Buffer) {
|
||||||
|
for p, c := range buf.CellMap {
|
||||||
|
b.Set(p.X, p.Y, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union returns a new Buffer formed by squeezing bufs into one Buffer
|
||||||
|
func Union(bufs ...Buffer) Buffer {
|
||||||
|
buf := NewBuffer()
|
||||||
|
for _, b := range bufs {
|
||||||
|
buf.Union(b)
|
||||||
|
}
|
||||||
|
buf.Align()
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point for adapting use, will be removed after resolving bridging.
|
||||||
|
type Point struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
Ch rune
|
||||||
|
Fg Attribute
|
||||||
|
Bg Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuffer returns a new Buffer
|
||||||
|
func NewBuffer() Buffer {
|
||||||
|
return Buffer{CellMap: make(map[image.Point]Cell)}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/gizak/termui"
|
import "github.com/gizak/termui"
|
||||||
|
import "github.com/gizak/termui/widget"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := termui.Init()
|
err := termui.Init()
|
||||||
@ -17,16 +18,23 @@ func main() {
|
|||||||
|
|
||||||
termui.UseTheme("helloworld")
|
termui.UseTheme("helloworld")
|
||||||
|
|
||||||
g0 := termui.NewGauge()
|
g0 := widget.NewGauge()
|
||||||
g0.Percent = 40
|
g0.Percent = 40
|
||||||
g0.Width = 50
|
g0.Width = 50
|
||||||
g0.Height = 3
|
g0.Height = 3
|
||||||
g0.Border.Label = "Slim Gauge"
|
g0.Border.Label = "Slim Gauge"
|
||||||
g0.BarColor = termui.ColorRed
|
g0.BarColor = termui.ColorRed
|
||||||
g0.Border.FgColor = termui.ColorWhite
|
g0.Border.Fg = termui.ColorWhite
|
||||||
g0.Border.LabelFgColor = termui.ColorCyan
|
g0.Border.LabelFgClr = termui.ColorCyan
|
||||||
|
|
||||||
g2 := termui.NewGauge()
|
gg := termui.NewBlock()
|
||||||
|
gg.Width = 50
|
||||||
|
gg.Height = 5
|
||||||
|
gg.Y = 12
|
||||||
|
gg.Border.Label = "TEST"
|
||||||
|
gg.Align()
|
||||||
|
|
||||||
|
g2 := widget.NewGauge()
|
||||||
g2.Percent = 60
|
g2.Percent = 60
|
||||||
g2.Width = 50
|
g2.Width = 50
|
||||||
g2.Height = 3
|
g2.Height = 3
|
||||||
@ -34,9 +42,9 @@ func main() {
|
|||||||
g2.Y = 3
|
g2.Y = 3
|
||||||
g2.Border.Label = "Slim Gauge"
|
g2.Border.Label = "Slim Gauge"
|
||||||
g2.BarColor = termui.ColorYellow
|
g2.BarColor = termui.ColorYellow
|
||||||
g2.Border.FgColor = termui.ColorWhite
|
g2.Border.Fg = termui.ColorWhite
|
||||||
|
|
||||||
g1 := termui.NewGauge()
|
g1 := widget.NewGauge()
|
||||||
g1.Percent = 30
|
g1.Percent = 30
|
||||||
g1.Width = 50
|
g1.Width = 50
|
||||||
g1.Height = 5
|
g1.Height = 5
|
||||||
@ -44,10 +52,10 @@ func main() {
|
|||||||
g1.Border.Label = "Big Gauge"
|
g1.Border.Label = "Big Gauge"
|
||||||
g1.PercentColor = termui.ColorYellow
|
g1.PercentColor = termui.ColorYellow
|
||||||
g1.BarColor = termui.ColorGreen
|
g1.BarColor = termui.ColorGreen
|
||||||
g1.Border.FgColor = termui.ColorWhite
|
g1.Border.Fg = termui.ColorWhite
|
||||||
g1.Border.LabelFgColor = termui.ColorMagenta
|
g1.Border.LabelFgClr = termui.ColorMagenta
|
||||||
|
|
||||||
termui.Render(g0, g1, g2)
|
termui.Render(g0, g1, g2, gg)
|
||||||
|
|
||||||
<-termui.EventCh()
|
<-termui.EventCh()
|
||||||
}
|
}
|
||||||
|
83
gauge.go
83
gauge.go
@ -1,83 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
package termui
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// Gauge is a progress bar like widget.
|
|
||||||
// A simple example:
|
|
||||||
/*
|
|
||||||
g := termui.NewGauge()
|
|
||||||
g.Percent = 40
|
|
||||||
g.Width = 50
|
|
||||||
g.Height = 3
|
|
||||||
g.Border.Label = "Slim Gauge"
|
|
||||||
g.BarColor = termui.ColorRed
|
|
||||||
g.PercentColor = termui.ColorBlue
|
|
||||||
*/
|
|
||||||
type Gauge struct {
|
|
||||||
Block
|
|
||||||
Percent int
|
|
||||||
BarColor Attribute
|
|
||||||
PercentColor Attribute
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGauge return a new gauge with current theme.
|
|
||||||
func NewGauge() *Gauge {
|
|
||||||
g := &Gauge{
|
|
||||||
Block: *NewBlock(),
|
|
||||||
PercentColor: theme.GaugePercent,
|
|
||||||
BarColor: theme.GaugeBar}
|
|
||||||
g.Width = 12
|
|
||||||
g.Height = 5
|
|
||||||
return g
|
|
||||||
}
|
|
||||||
|
|
||||||
// Buffer implements Bufferer interface.
|
|
||||||
func (g *Gauge) Buffer() []Point {
|
|
||||||
ps := g.Block.Buffer()
|
|
||||||
|
|
||||||
w := g.Percent * g.innerWidth / 100
|
|
||||||
s := strconv.Itoa(g.Percent) + "%"
|
|
||||||
rs := str2runes(s)
|
|
||||||
|
|
||||||
prx := g.innerX + g.innerWidth/2 - 1
|
|
||||||
pry := g.innerY + g.innerHeight/2
|
|
||||||
|
|
||||||
// plot bar
|
|
||||||
for i := 0; i < g.innerHeight; i++ {
|
|
||||||
for j := 0; j < w; j++ {
|
|
||||||
p := Point{}
|
|
||||||
p.X = g.innerX + j
|
|
||||||
p.Y = g.innerY + i
|
|
||||||
p.Ch = ' '
|
|
||||||
p.Bg = g.BarColor
|
|
||||||
if p.Bg == ColorDefault {
|
|
||||||
p.Bg |= AttrReverse
|
|
||||||
}
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// plot percentage
|
|
||||||
for i, v := range rs {
|
|
||||||
p := Point{}
|
|
||||||
p.X = prx + i
|
|
||||||
p.Y = pry
|
|
||||||
p.Ch = v
|
|
||||||
p.Fg = g.PercentColor
|
|
||||||
if w > g.innerWidth/2-1+i {
|
|
||||||
p.Bg = g.BarColor
|
|
||||||
if p.Bg == ColorDefault {
|
|
||||||
p.Bg |= AttrReverse
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
p.Bg = g.Block.BgColor
|
|
||||||
}
|
|
||||||
ps = append(ps, p)
|
|
||||||
}
|
|
||||||
return g.Block.chopOverflow(ps)
|
|
||||||
}
|
|
17
grid.go
17
grid.go
@ -160,8 +160,8 @@ func (r *Row) SetWidth(w int) {
|
|||||||
|
|
||||||
// Buffer implements Bufferer interface,
|
// Buffer implements Bufferer interface,
|
||||||
// recursively merge all widgets buffer
|
// recursively merge all widgets buffer
|
||||||
func (r *Row) Buffer() []Point {
|
func (r *Row) Buffer() Buffer {
|
||||||
merged := []Point{}
|
merged := Buffer{}
|
||||||
|
|
||||||
if r.isRenderableLeaf() {
|
if r.isRenderableLeaf() {
|
||||||
return r.Widget.Buffer()
|
return r.Widget.Buffer()
|
||||||
@ -169,13 +169,13 @@ func (r *Row) Buffer() []Point {
|
|||||||
|
|
||||||
// for those are not leaves but have a renderable widget
|
// for those are not leaves but have a renderable widget
|
||||||
if r.Widget != nil {
|
if r.Widget != nil {
|
||||||
merged = append(merged, r.Widget.Buffer()...)
|
merged.Union(r.Widget.Buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect buffer from children
|
// collect buffer from children
|
||||||
if !r.isLeaf() {
|
if !r.isLeaf() {
|
||||||
for _, c := range r.Cols {
|
for _, c := range r.Cols {
|
||||||
merged = append(merged, c.Buffer()...)
|
merged.Union(c.Buffer())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,12 +267,13 @@ func (g *Grid) Align() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Buffer implments Bufferer interface.
|
// Buffer implments Bufferer interface.
|
||||||
func (g Grid) Buffer() []Point {
|
func (g Grid) Buffer() Buffer {
|
||||||
ps := []Point{}
|
buf := Buffer{}
|
||||||
|
|
||||||
for _, r := range g.Rows {
|
for _, r := range g.Rows {
|
||||||
ps = append(ps, r.Buffer()...)
|
buf.Union(r.Buffer())
|
||||||
}
|
}
|
||||||
return ps
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body corresponds to the entire terminal display region.
|
// Body corresponds to the entire terminal display region.
|
||||||
|
57
helper.go
57
helper.go
@ -148,3 +148,60 @@ func StringToAttribute(text string) Attribute {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TextCells returns a coloured text cells []Cell
|
||||||
|
func TextCells(s string, fg, bg Attribute) []Cell {
|
||||||
|
cs := make([]Cell, 0, len(s))
|
||||||
|
|
||||||
|
// sequence := MarkdownTextRendererFactory{}.TextRenderer(s).Render(fg, bg)
|
||||||
|
// runes := []rune(sequence.NormalizedText)
|
||||||
|
runes := str2runes(s)
|
||||||
|
|
||||||
|
for n := range runes {
|
||||||
|
// point, _ := sequence.PointAt(n, 0, 0)
|
||||||
|
// cs = append(cs, Cell{point.Ch, point.Fg, point.Bg})
|
||||||
|
cs = append(cs, Cell{runes[n], fg, bg})
|
||||||
|
}
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width returns the actual screen space the cell takes (usually 1 or 2).
|
||||||
|
func (c Cell) Width() int {
|
||||||
|
return charWidth(c.Ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy return a copy of c
|
||||||
|
func (c Cell) Copy() Cell {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimTxCells trims the overflowed text cells sequence.
|
||||||
|
func TrimTxCells(cs []Cell, w int) []Cell {
|
||||||
|
if len(cs) <= w {
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
return cs[:w]
|
||||||
|
}
|
||||||
|
|
||||||
|
// DTrimTxCls trims the overflowed text cells sequence and append dots at the end.
|
||||||
|
func DTrimTxCls(cs []Cell, w int) []Cell {
|
||||||
|
l := len(cs)
|
||||||
|
if l <= 0 {
|
||||||
|
return []Cell{}
|
||||||
|
}
|
||||||
|
|
||||||
|
rt := make([]Cell, 0, w)
|
||||||
|
csw := 0
|
||||||
|
for i := 0; i < l && csw <= w; i++ {
|
||||||
|
c := cs[i]
|
||||||
|
cw := c.Width()
|
||||||
|
|
||||||
|
if cw+csw <= w {
|
||||||
|
rt = append(rt, c)
|
||||||
|
} else {
|
||||||
|
rt = append(rt, Cell{'…', c.Fg, c.Bg})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rt
|
||||||
|
}
|
||||||
|
28
point.go
28
point.go
@ -1,28 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
package termui
|
|
||||||
|
|
||||||
// Point stands for a single cell in terminal.
|
|
||||||
type Point struct {
|
|
||||||
Ch rune
|
|
||||||
Bg Attribute
|
|
||||||
Fg Attribute
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPoint(c rune, x, y int) (p Point) {
|
|
||||||
p.Ch = c
|
|
||||||
p.X = x
|
|
||||||
p.Y = y
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPointWithAttrs(c rune, x, y int, fg, bg Attribute) Point {
|
|
||||||
p := newPoint(c, x, y)
|
|
||||||
p.Bg = bg
|
|
||||||
p.Fg = fg
|
|
||||||
return p
|
|
||||||
}
|
|
17
render.go
17
render.go
@ -8,7 +8,7 @@ import tm "github.com/nsf/termbox-go"
|
|||||||
|
|
||||||
// Bufferer should be implemented by all renderable components.
|
// Bufferer should be implemented by all renderable components.
|
||||||
type Bufferer interface {
|
type Bufferer interface {
|
||||||
Buffer() []Point
|
Buffer() Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes termui library. This function should be called before any others.
|
// Init initializes termui library. This function should be called before any others.
|
||||||
@ -46,13 +46,18 @@ func TermHeight() int {
|
|||||||
|
|
||||||
// Render renders all Bufferer in the given order from left to right,
|
// Render renders all Bufferer in the given order from left to right,
|
||||||
// right could overlap on left ones.
|
// right could overlap on left ones.
|
||||||
func Render(rs ...Bufferer) {
|
func Render(bs ...Bufferer) {
|
||||||
|
// set tm bg
|
||||||
tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
|
tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
|
||||||
for _, r := range rs {
|
for _, b := range bs {
|
||||||
buf := r.Buffer()
|
buf := b.Buffer()
|
||||||
for _, v := range buf {
|
// set cels in buf
|
||||||
tm.SetCell(v.X, v.Y, v.Ch, toTmAttr(v.Fg), toTmAttr(v.Bg))
|
for p, c := range buf.CellMap {
|
||||||
|
if true { //}p.In(buf.Area) {
|
||||||
|
tm.SetCell(p.X, p.Y, c.Ch, toTmAttr(c.Fg), toTmAttr(c.Bg))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// render
|
||||||
tm.Flush()
|
tm.Flush()
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
//+build ignore
|
||||||
|
|
||||||
package termui
|
package termui
|
||||||
|
|
||||||
import (
|
import (
|
67
widget/gauge.go
Normal file
67
widget/gauge.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package widget
|
||||||
|
|
||||||
|
import "github.com/gizak/termui"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// Gauge is a progress bar like widget.
|
||||||
|
// A simple example:
|
||||||
|
/*
|
||||||
|
g := termui.NewGauge()
|
||||||
|
g.Percent = 40
|
||||||
|
g.Width = 50
|
||||||
|
g.Height = 3
|
||||||
|
g.Border.Label = "Slim Gauge"
|
||||||
|
g.BarColor = termui.ColorRed
|
||||||
|
g.PercentColor = termui.ColorBlue
|
||||||
|
*/
|
||||||
|
type Gauge struct {
|
||||||
|
termui.Block
|
||||||
|
Percent int
|
||||||
|
BarColor termui.Attribute
|
||||||
|
PercentColor termui.Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGauge return a new gauge with current theme.
|
||||||
|
func NewGauge() *Gauge {
|
||||||
|
g := &Gauge{
|
||||||
|
Block: *termui.NewBlock(),
|
||||||
|
PercentColor: termui.Theme().GaugePercent,
|
||||||
|
BarColor: termui.Theme().GaugeBar}
|
||||||
|
g.Width = 12
|
||||||
|
g.Height = 3
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer implements Bufferer interface.
|
||||||
|
func (g *Gauge) Buffer() termui.Buffer {
|
||||||
|
buf := g.Block.Buffer()
|
||||||
|
|
||||||
|
inner := g.InnerBounds()
|
||||||
|
w := g.Percent * (inner.Dx() + 1) / 100
|
||||||
|
s := strconv.Itoa(g.Percent) + "%"
|
||||||
|
tx := termui.TextCells(s, g.PercentColor, g.Bg)
|
||||||
|
|
||||||
|
prx := inner.Min.X + (inner.Dx()+1)/2 - 1
|
||||||
|
pry := inner.Min.Y + (inner.Dy()+1)/2
|
||||||
|
|
||||||
|
// plot bar
|
||||||
|
for i := 0; i <= inner.Dy(); i++ {
|
||||||
|
for j := 0; j < w; j++ {
|
||||||
|
c := termui.Cell{' ', g.BarColor, g.BarColor}
|
||||||
|
buf.Set(inner.Min.X+j, inner.Min.Y+i, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// plot percentage
|
||||||
|
for i, v := range tx {
|
||||||
|
if w > (inner.Dx()+1)/2-1+i {
|
||||||
|
v.Bg = g.BarColor
|
||||||
|
}
|
||||||
|
buf.Set(prx+i, pry, v)
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
// Copyright 2015 Zack Guo <gizak@icloud.com>. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT license that can
|
// Use of this source code is governed by a MIT license that can
|
||||||
// be found in the LICENSE file.
|
// be found in the LICENSE file.
|
Loading…
Reference in New Issue
Block a user