Move widget back to root

This commit is contained in:
gizak 2015-10-07 14:25:59 -04:00
parent 196d9aae34
commit e0dec9dbb9
17 changed files with 344 additions and 223 deletions

View File

@ -7,7 +7,6 @@
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()
@ -16,54 +15,57 @@ func main() {
} }
defer termui.Close() defer termui.Close()
termui.UseTheme("helloworld") //termui.UseTheme("helloworld")
g0 := widget.NewGauge() g0 := termui.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.BorderLabel = "Slim Gauge"
g0.BarColor = termui.ColorRed g0.BarColor = termui.ColorRed
g0.Border.Fg = termui.ColorWhite g0.BorderFg = termui.ColorWhite
g0.Border.LabelFgClr = termui.ColorCyan g0.BorderLabelFg = termui.ColorCyan
gg := termui.NewBlock() gg := termui.NewBlock()
gg.Width = 50 gg.Width = 50
gg.Height = 5 gg.Height = 5
gg.Y = 12 gg.Y = 12
gg.Border.Label = "TEST" gg.BorderLabel = "TEST"
gg.Align() gg.Align()
g2 := widget.NewGauge() g2 := termui.NewGauge()
g2.Percent = 60 g2.Percent = 60
g2.Width = 50 g2.Width = 50
g2.Height = 3 g2.Height = 3
g2.PercentColor = termui.ColorBlue g2.PercentColor = termui.ColorBlue
g2.Y = 3 g2.Y = 3
g2.Border.Label = "Slim Gauge" g2.BorderLabel = "Slim Gauge"
g2.BarColor = termui.ColorYellow g2.BarColor = termui.ColorYellow
g2.Border.Fg = termui.ColorWhite g2.BorderFg = termui.ColorWhite
g1 := widget.NewGauge() g1 := termui.NewGauge()
g1.Percent = 30 g1.Percent = 30
g1.Width = 50 g1.Width = 50
g1.Height = 5 g1.Height = 5
g1.Y = 6 g1.Y = 6
g1.Border.Label = "Big Gauge" g1.BorderLabel = "Big Gauge"
g1.PercentColor = termui.ColorYellow g1.PercentColor = termui.ColorYellow
g1.BarColor = termui.ColorGreen g1.BarColor = termui.ColorGreen
g1.Border.Fg = termui.ColorWhite g1.BorderFg = termui.ColorWhite
g1.Border.LabelFgClr = termui.ColorMagenta g1.BorderLabelFg = termui.ColorMagenta
g3 := termui.NewGauge() g3 := termui.NewGauge()
g3.Percent = 50 g3.Percent = 50
g3.Width = 50 g3.Width = 50
g3.Height = 3 g3.Height = 3
g3.Y = 11 g3.Y = 11
g3.Border.Label = "Gauge with custom label" g3.BorderLabel = "Gauge with custom label"
g3.Label = "{{percent}}% (100MBs free)" g3.Label = "{{percent}}% (100MBs free)"
g3.LabelAlign = termui.AlignRight g3.LabelAlign = termui.AlignRight
termui.Render(g0, g1, g2, g3) termui.Render(g0, g1, g2, g3)
<-termui.EventCh() termui.Handle("/sys/kbd/q", func(termui.Event) {
termui.StopLoop()
})
termui.Loop()
} }

View File

@ -41,16 +41,16 @@ type BarChart struct {
// NewBarChart returns a new *BarChart with current theme. // NewBarChart returns a new *BarChart with current theme.
func NewBarChart() *BarChart { func NewBarChart() *BarChart {
bc := &BarChart{Block: *NewBlock()} bc := &BarChart{Block: *NewBlock()}
bc.BarColor = theme.BarChartBar bc.BarColor = ThemeAttr("barchart.bar.bg")
bc.NumColor = theme.BarChartNum bc.NumColor = ThemeAttr("barchart.num.fg")
bc.TextColor = theme.BarChartText bc.TextColor = ThemeAttr("barchart.text.fg")
bc.BarGap = 1 bc.BarGap = 1
bc.BarWidth = 3 bc.BarWidth = 3
return bc return bc
} }
func (bc *BarChart) layout() { func (bc *BarChart) layout() {
bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
bc.labels = make([][]rune, bc.numBar) bc.labels = make([][]rune, bc.numBar)
bc.dataNum = make([][]rune, len(bc.Data)) bc.dataNum = make([][]rune, len(bc.Data))
@ -71,7 +71,7 @@ func (bc *BarChart) layout() {
bc.max = bc.Data[i] bc.max = bc.Data[i]
} }
} }
bc.scale = float64(bc.max) / float64(bc.innerHeight-1) bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
} }
func (bc *BarChart) SetMax(max int) { func (bc *BarChart) SetMax(max int) {
@ -82,8 +82,8 @@ func (bc *BarChart) SetMax(max int) {
} }
// Buffer implements Bufferer interface. // Buffer implements Bufferer interface.
func (bc *BarChart) Buffer() []Point { func (bc *BarChart) Buffer() Buffer {
ps := bc.Block.Buffer() buf := bc.Block.Buffer()
bc.layout() bc.layout()
for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ { for i := 0; i < bc.numBar && i < len(bc.Data) && i < len(bc.DataLabels); i++ {
@ -92,46 +92,48 @@ func (bc *BarChart) Buffer() []Point {
// plot bar // plot bar
for j := 0; j < bc.BarWidth; j++ { for j := 0; j < bc.BarWidth; j++ {
for k := 0; k < h; k++ { for k := 0; k < h; k++ {
p := Point{} c := Cell{
p.Ch = ' ' Ch: ' ',
p.Bg = bc.BarColor Bg: bc.BarColor,
if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
p.Bg |= AttrReverse
} }
p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j if bc.BarColor == ColorDefault { // when color is default, space char treated as transparent!
p.Y = bc.innerY + bc.innerHeight - 2 - k c.Bg |= AttrReverse
ps = append(ps, p) }
x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k
buf.Set(x, y, c)
} }
} }
// plot text // plot text
for j, k := 0, 0; j < len(bc.labels[i]); j++ { for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j]) w := charWidth(bc.labels[i][j])
p := Point{} c := Cell{}
p.Ch = bc.labels[i][j] p.Ch = bc.labels[i][j]
p.Bg = bc.BgColor p.Bg = bc.BgColor
p.Fg = bc.TextColor p.Fg = bc.TextColor
p.Y = bc.innerY + bc.innerHeight - 1 p.Y = bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
p.X = bc.innerX + oftX + k p.X = bc.innerArea.Min.X + oftX + k
ps = append(ps, p) ps = append(ps, p)
k += w k += w
} }
// plot num // plot num
for j := 0; j < len(bc.dataNum[i]); j++ { for j := 0; j < len(bc.dataNum[i]); j++ {
p := Point{} c := Cell{
p.Ch = bc.dataNum[i][j] Ch: bc.dataNum[i][j],
p.Fg = bc.NumColor Fg: bc.NumColor,
p.Bg = bc.BarColor Bg: bc.BarColor,
}
if bc.BarColor == ColorDefault { // the same as above if bc.BarColor == ColorDefault { // the same as above
p.Bg |= AttrReverse c.Bg |= AttrReverse
} }
if h == 0 { if h == 0 {
p.Bg = bc.BgColor c.Bg = bc.BgColor
} }
p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j
p.Y = bc.innerY + bc.innerHeight - 2 y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
ps = append(ps, p) buf.Set(x, y, c)
} }
} }
return bc.Block.chopOverflow(ps) return buf
} }

View File

@ -119,6 +119,7 @@ type Block struct {
PaddingBottom int PaddingBottom int
PaddingLeft int PaddingLeft int
PaddingRight int PaddingRight int
id string
} }
// NewBlock returns a *Block which inherits styles from current theme. // NewBlock returns a *Block which inherits styles from current theme.
@ -137,9 +138,14 @@ func NewBlock() *Block {
b.Bg = ThemeAttr("block.bg") b.Bg = ThemeAttr("block.bg")
b.Width = 2 b.Width = 2
b.Height = 2 b.Height = 2
b.id = GenId()
return &b return &b
} }
func (b Block) Id() string {
return b.id
}
// Align computes box model // Align computes box model
func (b *Block) Align() { func (b *Block) Align() {
b.area.Min.X = b.X b.area.Min.X = b.X

View File

@ -152,6 +152,7 @@ type EvtStream struct {
wg sync.WaitGroup wg sync.WaitGroup
sigStopLoop chan Event sigStopLoop chan Event
Handlers map[string]func(Event) Handlers map[string]func(Event)
hook func(Event)
} }
func NewEvtStream() *EvtStream { func NewEvtStream() *EvtStream {
@ -209,10 +210,10 @@ func (es *EvtStream) Handle(path string, handler func(Event)) {
es.Handlers[cleanPath(path)] = handler es.Handlers[cleanPath(path)] = handler
} }
func (es *EvtStream) match(path string) string { func findMatch(mux map[string]func(Event), path string) string {
n := -1 n := -1
pattern := "" pattern := ""
for m := range es.Handlers { for m := range mux {
if !isPathMatch(m, path) { if !isPathMatch(m, path) {
continue continue
} }
@ -222,11 +223,28 @@ func (es *EvtStream) match(path string) string {
} }
} }
return pattern return pattern
}
func (es *EvtStream) match(path string) string {
return findMatch(es.Handlers, path)
}
/*
var internalHandlers = make(map[string]func(Event))
func initInternalHandling() {
}
*/
func (es *EvtStream) Hook(f func(Event)) {
es.hook = f
} }
func (es *EvtStream) Loop() { func (es *EvtStream) Loop() {
for e := range es.stream { for e := range es.stream {
if e.Path == "/sig/stoploop" { switch e.Path {
case "/sig/stoploop":
return return
} }
go func(a Event) { go func(a Event) {
@ -236,6 +254,9 @@ func (es *EvtStream) Loop() {
es.Handlers[pattern](a) es.Handlers[pattern](a)
} }
}(e) }(e)
if es.hook != nil {
es.hook(e)
}
} }
} }

View File

@ -44,8 +44,8 @@ type Gauge struct {
func NewGauge() *Gauge { func NewGauge() *Gauge {
g := &Gauge{ g := &Gauge{
Block: *NewBlock(), Block: *NewBlock(),
PercentColor: theme.GaugePercent, PercentColor: ThemeAttr("gauge.percent.fg"),
BarColor: theme.GaugeBar, BarColor: ThemeAttr("gauge.bar.bg"),
Label: "{{percent}}%", Label: "{{percent}}%",
LabelAlign: AlignCenter, LabelAlign: AlignCenter,
} }
@ -56,28 +56,26 @@ func NewGauge() *Gauge {
} }
// Buffer implements Bufferer interface. // Buffer implements Bufferer interface.
func (g *Gauge) Buffer() []Point { func (g *Gauge) Buffer() Buffer {
ps := g.Block.Buffer() buf := g.Block.Buffer()
// plot bar // plot bar
w := g.Percent * g.innerWidth / 100 w := g.Percent * g.innerArea.Dx() / 100
for i := 0; i < g.innerHeight; i++ { for i := 0; i < g.innerArea.Dy(); i++ {
for j := 0; j < w; j++ { for j := 0; j < w; j++ {
p := Point{} c := Cell{}
p.X = g.innerX + j c.Ch = ' '
p.Y = g.innerY + i c.Bg = g.BarColor
p.Ch = ' ' if c.Bg == ColorDefault {
p.Bg = g.BarColor c.Bg |= AttrReverse
if p.Bg == ColorDefault {
p.Bg |= AttrReverse
} }
ps = append(ps, p) buf.Set(g.innerArea.Min.X+j, g.innerArea.Min.Y+i, c)
} }
} }
// plot percentage // plot percentage
s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1)
pry := g.innerY + g.innerHeight/2 pry := g.innerArea.Min.Y + g.innerArea.Dy()/2
rs := str2runes(s) rs := str2runes(s)
var pos int var pos int
switch g.LabelAlign { switch g.LabelAlign {
@ -85,29 +83,29 @@ func (g *Gauge) Buffer() []Point {
pos = 0 pos = 0
case AlignCenter: case AlignCenter:
pos = (g.innerWidth - strWidth(s)) / 2 pos = (g.innerArea.Dx() - strWidth(s)) / 2
case AlignRight: case AlignRight:
pos = g.innerWidth - strWidth(s) pos = g.innerArea.Dx() - strWidth(s)
} }
for i, v := range rs { for i, v := range rs {
p := Point{} c := Cell{
p.X = 1 + pos + i Ch: v,
p.Y = pry Fg: g.PercentColor,
p.Ch = v }
p.Fg = g.PercentColor
if w+g.innerX > pos+i { if w+g.innerArea.Min.X > pos+i {
p.Bg = g.BarColor c.Bg = g.BarColor
if p.Bg == ColorDefault { if c.Bg == ColorDefault {
p.Bg |= AttrReverse c.Bg |= AttrReverse
} }
} else { } else {
p.Bg = g.Block.BgColor c.Bg = g.Block.Bg
} }
ps = append(ps, p) buf.Set(1+pos+i, pry, c)
} }
return g.Block.chopOverflow(ps) return buf
} }

View File

@ -275,3 +275,5 @@ func (g Grid) Buffer() Buffer {
} }
return buf return buf
} }
var Body = NewGrid()

View File

@ -89,8 +89,8 @@ func NewLineChart() *LineChart {
// one cell contains two data points // one cell contains two data points
// so the capicity is 2x as dot-mode // so the capicity is 2x as dot-mode
func (lc *LineChart) renderBraille() []Point { func (lc *LineChart) renderBraille() Buffer {
ps := []Point{} buf := NewBuffer()
// return: b -> which cell should the point be in // return: b -> which cell should the point be in
// m -> in the cell, divided into 4 equal height levels, which subcell? // m -> in the cell, divided into 4 equal height levels, which subcell?
@ -106,44 +106,48 @@ func (lc *LineChart) renderBraille() []Point {
b1, m1 := getPos(lc.Data[2*i+1]) b1, m1 := getPos(lc.Data[2*i+1])
if b0 == b1 { if b0 == b1 {
p := Point{} c := Cell{
p.Ch = braillePatterns[[2]int{m0, m1}] Ch: braillePatterns[[2]int{m0, m1}],
p.Bg = lc.BgColor Bg: lc.BgColor,
p.Fg = lc.LineColor Fg: lc.LineColor,
p.Y = lc.innerY + lc.innerHeight - 3 - b0 }
p.X = lc.innerX + lc.labelYSpace + 1 + i y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0
ps = append(ps, p) x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
buf.Set(x, y, c)
} else { } else {
p0 := newPointWithAttrs(lSingleBraille[m0], c0 := Cell{Ch: lSingleBraille[m0],
lc.innerX+lc.labelYSpace+1+i, Fg: lc.LineColor,
lc.innerY+lc.innerHeight-3-b0, Bg: lc.BgColor}
lc.LineColor, x0 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
lc.BgColor) y0 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b0
p1 := newPointWithAttrs(rSingleBraille[m1], buf.Set(x0, y0, c0)
lc.innerX+lc.labelYSpace+1+i,
lc.innerY+lc.innerHeight-3-b1, c1 := Cell{Ch: rSingleBraille[m1],
lc.LineColor, Fg: lc.LineColor,
lc.BgColor) Bg: lc.Bg}
ps = append(ps, p0, p1) x1 := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
y1 := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - b1
buf.Set(x1, y1, c1)
} }
} }
return ps return buf
} }
func (lc *LineChart) renderDot() []Point { func (lc *LineChart) renderDot() Buffer {
ps := []Point{} buf := NewBuffer()
for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ { for i := 0; i < len(lc.Data) && i < lc.axisXWidth; i++ {
p := Point{} c := Cell{
p.Ch = lc.DotStyle Ch: lc.DotStyle,
p.Fg = lc.LineColor Fg: lc.LineColor,
p.Bg = lc.BgColor Bg: lc.BgColor,
p.X = lc.innerX + lc.labelYSpace + 1 + i }
p.Y = lc.innerY + lc.innerHeight - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5) x := lc.innerArea.Min.X + lc.labelYSpace + 1 + i
ps = append(ps, p) y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 3 - int((lc.Data[i]-lc.bottomValue)/lc.scale+0.5)
buf.Set(x, y, c)
} }
return ps return buf
} }
func (lc *LineChart) calcLabelX() { func (lc *LineChart) calcLabelX() {
@ -222,9 +226,9 @@ func (lc *LineChart) calcLayout() {
lc.maxY = lc.Data[0] lc.maxY = lc.Data[0]
// valid visible range // valid visible range
vrange := lc.innerWidth vrange := lc.innerArea.Dx()
if lc.Mode == "braille" { if lc.Mode == "braille" {
vrange = 2 * lc.innerWidth vrange = 2 * lc.innerArea.Dx()
} }
if vrange > len(lc.Data) { if vrange > len(lc.Data) {
vrange = len(lc.Data) vrange = len(lc.Data)
@ -249,40 +253,30 @@ func (lc *LineChart) calcLayout() {
lc.topValue = lc.maxY + 0.2*span lc.topValue = lc.maxY + 0.2*span
} }
lc.axisYHeight = lc.innerHeight - 2 lc.axisYHeight = lc.innerArea.Dy() - 2
lc.calcLabelY() lc.calcLabelY()
lc.axisXWidth = lc.innerWidth - 1 - lc.labelYSpace lc.axisXWidth = lc.innerArea.Dx() - 1 - lc.labelYSpace
lc.calcLabelX() lc.calcLabelX()
lc.drawingX = lc.innerX + 1 + lc.labelYSpace lc.drawingX = lc.innerArea.Min.X + 1 + lc.labelYSpace
lc.drawingY = lc.innerY lc.drawingY = lc.innerArea.Min.Y
} }
func (lc *LineChart) plotAxes() []Point { func (lc *LineChart) plotAxes() Buffer {
origY := lc.innerY + lc.innerHeight - 2 buf := NewBuffer()
origX := lc.innerX + lc.labelYSpace
ps := []Point{newPointWithAttrs(ORIGIN, origX, origY, lc.AxesColor, lc.BgColor)} origY := lc.innerArea.Min.Y + lc.innerArea.Dy() - 2
origX := lc.innerArea.Min.X + lc.labelYSpace
buf.Set(origX, origY, Cell{Ch: ORIGIN, Fg: lc.AxesColor, Bg: lc.Bg})
for x := origX + 1; x < origX+lc.axisXWidth; x++ { for x := origX + 1; x < origX+lc.axisXWidth; x++ {
p := Point{} buf.Set(x, origY, Cell{Ch: HDASH, Fg: lc.AxesColor, Bg: lc.Bg})
p.X = x
p.Y = origY
p.Bg = lc.BgColor
p.Fg = lc.AxesColor
p.Ch = HDASH
ps = append(ps, p)
} }
for dy := 1; dy <= lc.axisYHeight; dy++ { for dy := 1; dy <= lc.axisYHeight; dy++ {
p := Point{} buf.Set(origX, origY-dy, Cell{Ch: VDASH, Fg: lc.AxesColor, Bg: lc.Bg})
p.X = origX
p.Y = origY - dy
p.Bg = lc.BgColor
p.Fg = lc.AxesColor
p.Ch = VDASH
ps = append(ps, p)
} }
// x label // x label
@ -292,13 +286,14 @@ func (lc *LineChart) plotAxes() []Point {
break break
} }
for j, r := range rs { for j, r := range rs {
p := Point{} c := Cell{
p.Ch = r Ch: r,
p.Fg = lc.AxesColor Fg: lc.AxesColor,
p.Bg = lc.BgColor Bg: lc.BgColor,
p.X = origX + oft + j }
p.Y = lc.innerY + lc.innerHeight - 1 x := origX + oft + j
ps = append(ps, p) y := lc.innerArea.Min.Y + lc.innerArea.Dy() - 1
buf.Set(x, y, c)
} }
oft += len(rs) + lc.axisXLebelGap oft += len(rs) + lc.axisXLebelGap
} }
@ -306,33 +301,31 @@ func (lc *LineChart) plotAxes() []Point {
// y labels // y labels
for i, rs := range lc.labelY { for i, rs := range lc.labelY {
for j, r := range rs { for j, r := range rs {
p := Point{} buf.Set(
p.Ch = r lc.innerArea.Min.X+j,
p.Fg = lc.AxesColor origY-i*(lc.axisYLebelGap+1),
p.Bg = lc.BgColor Cell{Ch: r, Fg: lc.AxesColor, Bg: lc.Bg})
p.X = lc.innerX + j
p.Y = origY - i*(lc.axisYLebelGap+1)
ps = append(ps, p)
} }
} }
return ps return buf
} }
// Buffer implements Bufferer interface. // Buffer implements Bufferer interface.
func (lc *LineChart) Buffer() []Point { func (lc *LineChart) Buffer() []Point {
ps := lc.Block.Buffer() buf := lc.Block.Buffer()
if lc.Data == nil || len(lc.Data) == 0 { if lc.Data == nil || len(lc.Data) == 0 {
return ps return ps
} }
lc.calcLayout() lc.calcLayout()
ps = append(ps, lc.plotAxes()...) buf.Merge(lc.plotAxes())
if lc.Mode == "dot" { if lc.Mode == "dot" {
ps = append(ps, lc.renderDot()...) buf.Merge(lc.renderDot())
} else { } else {
ps = append(ps, lc.renderBraille()...) buf.Merge(ps, lc.renderBraille())
} }
return lc.Block.chopOverflow(ps) return buf
} }

View File

@ -53,7 +53,7 @@ func (l *List) Buffer() []Point {
buffer := l.Block.Buffer() buffer := l.Block.Buffer()
breakLoop := func(y int) bool { breakLoop := func(y int) bool {
return y+1 > l.innerHeight return y+1 > l.innerArea.Dy()
} }
y := 0 y := 0
@ -65,9 +65,9 @@ MainLoop:
sequence := renderer.Render(bg, fg) sequence := renderer.Render(bg, fg)
for n := range []rune(sequence.NormalizedText) { for n := range []rune(sequence.NormalizedText) {
point, width := sequence.PointAt(n, x+l.innerX, y+l.innerY) point, width := sequence.PointAt(n, x+l.innerArea.Min.X, y+l.innerArea.Min.Y)
if width+x <= l.innerWidth { if width+x <= l.innerArea.Dx() {
buffer = append(buffer, point) buffer = append(buffer, point)
x += width x += width
} else { } else {
@ -79,8 +79,8 @@ MainLoop:
x = 0 x = 0
} else { } else {
dotR := []rune(dot)[0] dotR := []rune(dot)[0]
dotX := l.innerWidth + l.innerX - charWidth(dotR) dotX := l.innerArea.Dx() + l.innerArea.Min.X - charWidth(dotR)
p := newPointWithAttrs(dotR, dotX, y+l.innerY, bg, fg) p := newPointWithAttrs(dotR, dotX, y+l.innerArea.Min.Y, bg, fg)
buffer = append(buffer, p) buffer = append(buffer, p)
break break
} }

View File

@ -48,16 +48,16 @@ type MBarChart struct {
// NewBarChart returns a new *BarChart with current theme. // NewBarChart returns a new *BarChart with current theme.
func NewMBarChart() *MBarChart { func NewMBarChart() *MBarChart {
bc := &MBarChart{Block: *NewBlock()} bc := &MBarChart{Block: *NewBlock()}
bc.BarColor[0] = theme.MBarChartBar bc.BarColor[0] = ThemeAttr("mbarchart.bar.bg")
bc.NumColor[0] = theme.MBarChartNum bc.NumColor[0] = ThemeAttr("mbarchart.num.fg")
bc.TextColor = theme.MBarChartText bc.TextColor = ThemeAttr("mbarchart.text.fg")
bc.BarGap = 1 bc.BarGap = 1
bc.BarWidth = 3 bc.BarWidth = 3
return bc return bc
} }
func (bc *MBarChart) layout() { func (bc *MBarChart) layout() {
bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) bc.numBar = bc.innerArea.Dx() / (bc.BarGap + bc.BarWidth)
bc.labels = make([][]rune, bc.numBar) bc.labels = make([][]rune, bc.numBar)
DataLen := 0 DataLen := 0
LabelLen := len(bc.DataLabels) LabelLen := len(bc.DataLabels)
@ -129,9 +129,9 @@ func (bc *MBarChart) layout() {
if bc.ShowScale { if bc.ShowScale {
s := fmt.Sprintf("%d", bc.max) s := fmt.Sprintf("%d", bc.max)
bc.maxScale = trimStr2Runes(s, len(s)) bc.maxScale = trimStr2Runes(s, len(s))
bc.scale = float64(bc.max) / float64(bc.innerHeight-2) bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-2)
} else { } else {
bc.scale = float64(bc.max) / float64(bc.innerHeight-1) bc.scale = float64(bc.max) / float64(bc.innerArea.Dy()-1)
} }
} }
@ -144,8 +144,8 @@ func (bc *MBarChart) SetMax(max int) {
} }
// Buffer implements Bufferer interface. // Buffer implements Bufferer interface.
func (bc *MBarChart) Buffer() []Point { func (bc *MBarChart) Buffer() Buffer {
ps := bc.Block.Buffer() buf := bc.Block.Buffer()
bc.layout() bc.layout()
var oftX int var oftX int
@ -157,15 +157,17 @@ func (bc *MBarChart) Buffer() []Point {
// plot bars // plot bars
for j := 0; j < bc.BarWidth; j++ { for j := 0; j < bc.BarWidth; j++ {
for k := 0; k < h; k++ { for k := 0; k < h; k++ {
p := Point{} c := Cell{
p.Ch = ' ' Ch: ' ',
p.Bg = bc.BarColor[i1] Bg: bc.BarColor[i1],
if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
p.Bg |= AttrReverse
} }
p.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j if bc.BarColor[i1] == ColorDefault { // when color is default, space char treated as transparent!
p.Y = bc.innerY + bc.innerHeight - 2 - k - ph c.Bg |= AttrReverse
ps = append(ps, p) }
x := bc.innerArea.Min.X + i*(bc.BarWidth+bc.BarGap) + j
y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - k - ph
buf.Set(x, y, c)
} }
} }
ph += h ph += h
@ -173,13 +175,14 @@ func (bc *MBarChart) Buffer() []Point {
// plot text // plot text
for j, k := 0, 0; j < len(bc.labels[i]); j++ { for j, k := 0, 0; j < len(bc.labels[i]); j++ {
w := charWidth(bc.labels[i][j]) w := charWidth(bc.labels[i][j])
p := Point{} c := Cell{
p.Ch = bc.labels[i][j] Ch: bc.labels[i][j],
p.Bg = bc.BgColor Bg: bc.Bg,
p.Fg = bc.TextColor Fg: bc.TextColor,
p.Y = bc.innerY + bc.innerHeight - 1 }
p.X = bc.innerX + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 1
ps = append(ps, p) x := bc.innerArea.Max.X + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k
buf.Set(x, y, c)
k += w k += w
} }
// plot num // plot num
@ -187,19 +190,20 @@ func (bc *MBarChart) Buffer() []Point {
for i1 := 0; i1 < bc.numStack; i1++ { for i1 := 0; i1 < bc.numStack; i1++ {
h := int(float64(bc.Data[i1][i]) / bc.scale) h := int(float64(bc.Data[i1][i]) / bc.scale)
for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ { for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ {
p := Point{} c := Cell{
p.Ch = bc.dataNum[i1][i][j] Ch: bc.dataNum[i1][i][j],
p.Fg = bc.NumColor[i1] Fg: bc.NumColor[i1],
p.Bg = bc.BarColor[i1] Bg: bc.BarColor[i1],
}
if bc.BarColor[i1] == ColorDefault { // the same as above if bc.BarColor[i1] == ColorDefault { // the same as above
p.Bg |= AttrReverse c.Bg |= AttrReverse
} }
if h == 0 { if h == 0 {
p.Bg = bc.BgColor c.Bg = bc.Bg
} }
p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j x := bc.innerArea.Min.X + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j
p.Y = bc.innerY + bc.innerHeight - 2 - ph y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2 - ph
ps = append(ps, p) buf.Set(x, y, c)
} }
ph += h ph += h
} }
@ -208,26 +212,31 @@ func (bc *MBarChart) Buffer() []Point {
if bc.ShowScale { if bc.ShowScale {
//Currently bar graph only supprts data range from 0 to MAX //Currently bar graph only supprts data range from 0 to MAX
//Plot 0 //Plot 0
p := Point{} c := Cell{
p.Ch = '0' Ch: '0',
p.Bg = bc.BgColor Bg: bc.Bg,
p.Fg = bc.TextColor Fg: bc.TextColor,
p.Y = bc.innerY + bc.innerHeight - 2 }
p.X = bc.X
ps = append(ps, p) y := bc.innerArea.Min.Y + bc.innerArea.Dy() - 2
x := bc.X
buf.Set(x, y, c)
//Plot the maximum sacle value //Plot the maximum sacle value
for i := 0; i < len(bc.maxScale); i++ { for i := 0; i < len(bc.maxScale); i++ {
p := Point{} c := Cell{
p.Ch = bc.maxScale[i] Ch: bc.maxScale[i],
p.Bg = bc.BgColor Bg: bc.Bg,
p.Fg = bc.TextColor Fg: bc.TextColor,
p.Y = bc.innerY }
p.X = bc.X + i
ps = append(ps, p) y := bc.innerArea.Min.Y
x := bc.X + i
buf.Set(x, y, c)
} }
} }
return bc.Block.chopOverflow(ps) return buf
} }

View File

@ -41,20 +41,20 @@ func (p *Par) Buffer() []Point {
runes := []rune(sequence.NormalizedText) runes := []rune(sequence.NormalizedText)
y, x, n := 0, 0, 0 y, x, n := 0, 0, 0
for y < p.innerHeight && n < len(runes) { for y < p.innerArea.Dy() && n < len(runes) {
point, width := sequence.PointAt(n, x+p.innerX, y+p.innerY) point, width := sequence.PointAt(n, x+p.innerArea.Min.X, y+p.innerArea.Min.Y)
if runes[n] == '\n' || x+width > p.innerWidth { if runes[n] == '\n' || x+width > p.innerArea.Dx() {
y++ y++
x = 0 // set x = 0 x = 0 // set x = 0
if runes[n] == '\n' { if runes[n] == '\n' {
n++ n++
} }
if y >= p.innerHeight { if y >= p.innerArea.Dy() {
ps = append(ps, newPointWithAttrs('…', ps = append(ps, newPointWithAttrs('…',
p.innerX+p.innerWidth-1, p.innerArea.Min.X+p.innerArea.Dx()-1,
p.innerY+p.innerHeight-1, p.innerArea.Min.Y+p.innerArea.Dy()-1,
p.TextFgColor, p.TextBgColor)) p.TextFgColor, p.TextBgColor))
break break
} }

View File

@ -35,7 +35,13 @@ func Init() error {
DefaultEvtStream.Merge("termbox", NewSysEvtCh()) DefaultEvtStream.Merge("termbox", NewSysEvtCh())
DefaultEvtStream.Merge("timer", NewTimerCh(time.Second)) DefaultEvtStream.Merge("timer", NewTimerCh(time.Second))
DefaultEvtStream.Handle("/", DefualtHandler) DefaultEvtStream.Handle("/", DefualtHandler)
DefaultEvtStream.Handle("/sys/wnd/resize", func(e Event) {
w := e.Data.(EvtWnd)
Body.Width = w.Width
})
DefaultWgtMgr = NewWgtMgr()
DefaultEvtStream.Hook(DefaultWgtMgr.WgtHandlersHook())
return nil return nil
} }

View File

@ -69,13 +69,13 @@ func (sl *Sparklines) update() {
sl.Lines[i].displayHeight = v.Height + 1 sl.Lines[i].displayHeight = v.Height + 1
} }
} }
sl.displayWidth = sl.innerWidth sl.displayWidth = sl.innerArea.Dx()
// get how many lines gotta display // get how many lines gotta display
h := 0 h := 0
sl.displayLines = 0 sl.displayLines = 0
for _, v := range sl.Lines { for _, v := range sl.Lines {
if h+v.displayHeight <= sl.innerHeight { if h+v.displayHeight <= sl.innerArea.Dy() {
sl.displayLines++ sl.displayLines++
} else { } else {
break break
@ -107,21 +107,21 @@ func (sl *Sparklines) Buffer() []Point {
l := sl.Lines[i] l := sl.Lines[i]
data := l.Data data := l.Data
if len(data) > sl.innerWidth { if len(data) > sl.innerArea.Dx() {
data = data[len(data)-sl.innerWidth:] data = data[len(data)-sl.innerArea.Dx():]
} }
if l.Title != "" { if l.Title != "" {
rs := trimStr2Runes(l.Title, sl.innerWidth) rs := trimStr2Runes(l.Title, sl.innerArea.Dx())
oftX := 0 oftX := 0
for _, v := range rs { for _, v := range rs {
w := charWidth(v) w := charWidth(v)
p := Point{} c := Cell{}
p.Ch = v p.Ch = v
p.Fg = l.TitleColor p.Fg = l.TitleColor
p.Bg = sl.BgColor p.Bg = sl.BgColor
p.X = sl.innerX + oftX p.X = sl.innerArea.Min.X + oftX
p.Y = sl.innerY + oftY p.Y = sl.innerArea.Min.Y + oftY
ps = append(ps, p) ps = append(ps, p)
oftX += w oftX += w
} }
@ -132,18 +132,18 @@ func (sl *Sparklines) Buffer() []Point {
barCnt := h / 8 barCnt := h / 8
barMod := h % 8 barMod := h % 8
for jj := 0; jj < barCnt; jj++ { for jj := 0; jj < barCnt; jj++ {
p := Point{} c := Cell{}
p.X = sl.innerX + j p.X = sl.innerArea.Min.X + j
p.Y = sl.innerY + oftY + l.Height - jj p.Y = sl.innerArea.Min.Y + oftY + l.Height - jj
p.Ch = ' ' // => sparks[7] p.Ch = ' ' // => sparks[7]
p.Bg = l.LineColor p.Bg = l.LineColor
//p.Bg = sl.BgColor //p.Bg = sl.BgColor
ps = append(ps, p) ps = append(ps, p)
} }
if barMod != 0 { if barMod != 0 {
p := Point{} c := Cell{}
p.X = sl.innerX + j p.X = sl.innerArea.Min.X + j
p.Y = sl.innerY + oftY + l.Height - barCnt p.Y = sl.innerArea.Min.Y + oftY + l.Height - barCnt
p.Ch = sparks[barMod-1] p.Ch = sparks[barMod-1]
p.Fg = l.LineColor p.Fg = l.LineColor
p.Bg = sl.BgColor p.Bg = sl.BgColor

82
widget.go Normal file
View File

@ -0,0 +1,82 @@
package termui
import (
"fmt"
"sync"
)
// event mixins
type WgtMgr map[string]WgtInfo
type WgtInfo struct {
Handlers map[string]func(Event)
WgtRef Widget
Id string
}
type Widget interface {
Id() string
}
func NewWgtInfo(wgt Widget) WgtInfo {
return WgtInfo{
Handlers: make(map[string]func(Event)),
WgtRef: wgt,
Id: wgt.Id(),
}
}
func NewWgtMgr() WgtMgr {
wm := WgtMgr(make(map[string]WgtInfo))
return wm
}
func (wm WgtMgr) AddWgt(wgt Widget) {
wm[wgt.Id()] = NewWgtInfo(wgt)
}
func (wm WgtMgr) RmWgt(wgt Widget) {
wm.RmWgtById(wgt.Id())
}
func (wm WgtMgr) RmWgtById(id string) {
delete(wm, id)
}
func (wm WgtMgr) AddWgtHandler(id, path string, h func(Event)) {
if w, ok := wm[id]; ok {
w.Handlers[path] = h
}
}
func (wm WgtMgr) RmWgtHandler(id, path string) {
if w, ok := wm[id]; ok {
delete(w.Handlers, path)
}
}
var counter struct {
sync.RWMutex
count int
}
func GenId() string {
counter.Lock()
defer counter.Unlock()
counter.count += 1
return fmt.Sprintf("%d", counter.count)
}
func (wm WgtMgr) WgtHandlersHook() func(Event) {
return func(e Event) {
for _, v := range wm {
if k := findMatch(v.Handlers, e.Path); k != "" {
v.Handlers[k](e)
}
}
}
}
var DefaultWgtMgr WgtMgr