diff --git a/README.md b/README.md
index 6b63593..ec54eb7 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,9 @@ __Demo:__
## Usage
-Each component's layout is a bit like HTML block, which has border and padding.
+Each component's layout is a bit like HTML block (box model), which has border and padding.
-The `Border` property can be chosen to hide or display (with its border label), when it comes to display, in this case the space it takes is counted as padding space (i.e. `PaddingTop=PaddingBottom=PaddingLeft=PaddingRight=1`).
+The `Border` property can be chosen to hide or display (with its border label), when it comes to display, the label takes 1 padding space (i.e. in css: `padding: 1;`, innerHeight and innerWidth therefore shrunk by 1).
`````go
import ui "github.com/gizak/termui" // <- ui shortcut, optional
@@ -50,11 +50,29 @@ The `Border` property can be chosen to hide or display (with its border label),
}
`````
-Note that components can be overlapped (I'd rather call this as a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right).
+Note that components can be overlapped (I'd rather call this a feature...), `Render(rs ...Renderer)` renders its args from left to right (i.e. each component's weight is arising from left to right).
+
+## Themes
+
+All colors in all components call be changed at any time, while there provides some predefined color scheme.
+
+```
+// for now there are only two themes: default and helloworld
+termui.UseTheme("helloworld")
+
+// create components...
+```
+The `default ` theme's settings depend on the user's terminal color scheme, which is saying if your terminal default font color is white and background is white, it will be like:
+
+
+
+The `helloworld` color scheme drops in some colors!
+
+
## Widgets
-_APIs are subject to change, docs will be added after 2 or 3 commits_
+_APIs are subject to change, docs will be added after 1 or 2 commits_
## GoDoc
diff --git a/bar.go b/bar.go
index af6431d..18aa964 100644
--- a/bar.go
+++ b/bar.go
@@ -20,9 +20,9 @@ type BarChart struct {
func NewBarChart() *BarChart {
bc := &BarChart{Block: *NewBlock()}
- bc.BarColor = ColorCyan
- bc.NumColor = ColorWhite
- bc.TextColor = ColorWhite
+ bc.BarColor = theme.BarChartBar
+ bc.NumColor = theme.BarChartNum
+ bc.TextColor = theme.BarChartText
bc.BarGap = 1
bc.BarWidth = 3
return bc
@@ -62,6 +62,9 @@ func (bc *BarChart) Buffer() []Point {
p := Point{}
p.Ch = ' '
p.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
p.Y = bc.innerY + bc.innerHeight - 2 - k
ps = append(ps, p)
@@ -83,6 +86,9 @@ func (bc *BarChart) Buffer() []Point {
p.Ch = bc.dataNum[i][j]
p.Fg = bc.NumColor
p.Bg = bc.BarColor
+ if bc.BarColor == ColorDefault { // the same as above
+ p.Bg |= AttrReverse
+ }
if h == 0 {
p.Bg = bc.BgColor
}
diff --git a/block.go b/block.go
index bdab2b9..bd69544 100644
--- a/block.go
+++ b/block.go
@@ -1,5 +1,6 @@
package termui
+// basic struct, consider it as css: display:block
type Block struct {
X int
Y int
@@ -22,7 +23,12 @@ type Block struct {
func NewBlock() *Block {
d := Block{}
d.IsDisplay = true
- d.HasBorder = true
+ d.HasBorder = theme.HasBorder
+ d.Border.BgColor = theme.BorderBg
+ d.Border.FgColor = theme.BorderFg
+ d.Border.LabelBgColor = theme.BorderLabelTextBg
+ d.Border.LabelFgColor = theme.BorderLabelTextFg
+ d.BgColor = theme.BlockBg
d.Width = 2
d.Height = 2
return &d
diff --git a/chart.go b/chart.go
index c93338f..c2831c8 100644
--- a/chart.go
+++ b/chart.go
@@ -55,6 +55,8 @@ type LineChart struct {
func NewLineChart() *LineChart {
lc := &LineChart{Block: *NewBlock()}
+ lc.AxesColor = theme.LineChartAxes
+ lc.LineColor = theme.LineChartLine
lc.Mode = "braille"
lc.DotStyle = '•'
lc.axisXLebelGap = 2
diff --git a/example/dashboard.go b/example/dashboard.go
index 73b54ca..c7a2326 100644
--- a/example/dashboard.go
+++ b/example/dashboard.go
@@ -13,7 +13,7 @@ func main() {
}
defer ui.Close()
- p := ui.NewP(":PRESS q TO QUIT DEMO")
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
p.Height = 3
p.Width = 50
p.TextFgColor = ui.ColorWhite
@@ -96,7 +96,7 @@ func main() {
lc1 := ui.NewLineChart()
lc1.Border.Label = "Line Chart"
rndwalk := (func() []float64 {
- n := 100
+ n := 150
d := make([]float64, n)
for i := 1; i < n; i++ {
if i < 20 {
@@ -116,7 +116,7 @@ func main() {
lc1.AxesColor = ui.ColorWhite
lc1.LineColor = ui.ColorYellow | ui.AttrBold
- p1 := ui.NewP("Hey!\nI am a borderless block!")
+ p1 := ui.NewPar("Hey!\nI am a borderless block!")
p1.HasBorder = false
p1.Width = 26
p1.Height = 2
diff --git a/example/theme.go b/example/theme.go
new file mode 100644
index 0000000..5e0a332
--- /dev/null
+++ b/example/theme.go
@@ -0,0 +1,144 @@
+package main
+
+import ui "github.com/gizak/termui"
+import tm "github.com/nsf/termbox-go"
+import "math"
+
+import "time"
+
+func main() {
+ err := ui.Init()
+ if err != nil {
+ panic(err)
+ }
+ defer ui.Close()
+
+ ui.UseTheme("helloworld")
+
+ p := ui.NewPar(":PRESS q TO QUIT DEMO")
+ p.Height = 3
+ p.Width = 50
+ p.Border.Label = "Text Box"
+
+ strs := []string{"[0] gizak/termui", "[1] editbox.go", "[2] iterrupt.go", "[3] keyboard.go", "[4] output.go", "[5] random_out.go", "[6] dashboard.go", "[7] nsf/termbox-go"}
+ list := ui.NewList()
+ list.Items = strs
+ list.Border.Label = "List"
+ list.Height = 7
+ list.Width = 25
+ list.Y = 4
+
+ g := ui.NewGauge()
+ g.Percent = 50
+ g.Width = 50
+ g.Height = 3
+ g.Y = 11
+ g.Border.Label = "Gauge"
+
+ spark := ui.NewSparkline()
+ spark.Title = "srv 0:"
+ spdata := []int{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
+ spark.Data = spdata
+
+ spark1 := ui.NewSparkline()
+ spark1.Title = "srv 1:"
+ spark1.Data = spdata
+
+ sp := ui.NewSparklines(spark, spark1)
+ sp.Width = 25
+ sp.Height = 7
+ sp.Border.Label = "Sparkline"
+ sp.Y = 4
+ sp.X = 25
+
+ lc := ui.NewLineChart()
+ sinps := (func() []float64 {
+ n := 100
+ ps := make([]float64, n)
+ for i := range ps {
+ ps[i] = 1 + math.Sin(float64(i)/4)
+ }
+ return ps
+ })()
+
+ lc.Border.Label = "Line Chart"
+ lc.Data = sinps
+ lc.Width = 50
+ lc.Height = 11
+ lc.X = 0
+ lc.Y = 14
+ lc.Mode = "dot"
+
+ bc := ui.NewBarChart()
+ bcdata := []int{3, 2, 5, 3, 9, 5, 3, 2, 5, 8, 3, 2, 4, 5, 3, 2, 5, 7, 5, 3, 2, 6, 7, 4, 6, 3, 6, 7, 8, 3, 6, 4, 5, 3, 2, 4, 6, 4, 8, 5, 9, 4, 3, 6, 5, 3, 6}
+ bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"}
+ bc.Border.Label = "Bar Chart"
+ bc.Width = 26
+ bc.Height = 10
+ bc.X = 51
+ bc.Y = 0
+ bc.DataLabels = bclabels
+
+ lc1 := ui.NewLineChart()
+ lc1.Border.Label = "Line Chart"
+ rndwalk := (func() []float64 {
+ n := 150
+ d := make([]float64, n)
+ for i := 1; i < n; i++ {
+ if i < 20 {
+ d[i] = d[i-1] + 0.01
+ }
+ if i > 20 {
+ d[i] = d[i-1] - 0.05
+ }
+ }
+ return d
+ })()
+ lc1.Data = rndwalk
+ lc1.Width = 26
+ lc1.Height = 11
+ lc1.X = 51
+ lc1.Y = 14
+
+ p1 := ui.NewPar("Hey!\nI am a borderless block!")
+ p1.HasBorder = false
+ p1.Width = 26
+ p1.Height = 2
+ p1.X = 52
+ p1.Y = 11
+
+ draw := func(t int) {
+ g.Percent = t % 101
+ list.Items = strs[t%9:]
+ sp.Lines[0].Data = spdata[t%10:]
+ sp.Lines[1].Data = spdata[t/2%10:]
+ lc.Data = sinps[t/2:]
+ lc1.Data = rndwalk[t:]
+ bc.Data = bcdata[t/2%10:]
+ ui.Render(p, list, g, sp, lc, bc, lc1, p1)
+ }
+
+ evt := make(chan tm.Event)
+ go func() {
+ for {
+ evt <- tm.PollEvent()
+ }
+ }()
+
+ i := 0
+ for {
+ select {
+ case e := <-evt:
+ if e.Type == tm.EventKey && e.Ch == 'q' {
+ return
+ }
+ default:
+ draw(i)
+ i++
+ if i == 102 {
+ return
+ }
+ time.Sleep(time.Second / 2)
+ }
+ }
+}
diff --git a/example/themedefault.tiff b/example/themedefault.tiff
new file mode 100644
index 0000000..2f89b54
Binary files /dev/null and b/example/themedefault.tiff differ
diff --git a/example/themehelloworld.tiff b/example/themehelloworld.tiff
new file mode 100644
index 0000000..fbf0f24
Binary files /dev/null and b/example/themehelloworld.tiff differ
diff --git a/gauge.go b/gauge.go
index 0d8eff3..6df5c78 100644
--- a/gauge.go
+++ b/gauge.go
@@ -10,7 +10,10 @@ type Gauge struct {
}
func NewGauge() *Gauge {
- g := &Gauge{Block: *NewBlock(), PercentColor: ColorWhite, BarColor: ColorGreen}
+ g := &Gauge{
+ Block: *NewBlock(),
+ PercentColor: theme.GaugePercent,
+ BarColor: theme.GaugeBar}
g.Width = 12
g.Height = 5
return g
@@ -34,6 +37,9 @@ func (g *Gauge) Buffer() []Point {
p.Y = g.innerY + i
p.Ch = ' '
p.Bg = g.BarColor
+ if p.Bg == ColorDefault {
+ p.Bg |= AttrReverse
+ }
ps = append(ps, p)
}
}
@@ -47,6 +53,10 @@ func (g *Gauge) Buffer() []Point {
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
}
diff --git a/list.go b/list.go
index b61682f..3ad7d18 100644
--- a/list.go
+++ b/list.go
@@ -13,6 +13,8 @@ type List struct {
func NewList() *List {
l := &List{Block: *NewBlock()}
l.Overflow = "hidden"
+ l.ItemFgColor = theme.ListItemFg
+ l.ItemBgColor = theme.ListItemBg
return l
}
diff --git a/p.go b/p.go
index 3fd848f..c5dd936 100644
--- a/p.go
+++ b/p.go
@@ -1,17 +1,21 @@
package termui
-type P struct {
+type Par struct {
Block
Text string
TextFgColor Attribute
TextBgColor Attribute
}
-func NewP(s string) *P {
- return &P{Block: *NewBlock(), Text: s}
+func NewPar(s string) *Par {
+ return &Par{
+ Block: *NewBlock(),
+ Text: s,
+ TextFgColor: theme.ParTextFg,
+ TextBgColor: theme.ParTextBg}
}
-func (p *P) Buffer() []Point {
+func (p *Par) Buffer() []Point {
ps := p.Block.Buffer()
rs := str2runes(p.Text)
diff --git a/render.go b/render.go
index e138ea2..ef544ab 100644
--- a/render.go
+++ b/render.go
@@ -2,6 +2,7 @@ package termui
import tm "github.com/nsf/termbox-go"
+// all renderable components should implement this
type Bufferer interface {
Buffer() []Point
}
@@ -14,7 +15,9 @@ func Close() {
tm.Close()
}
+// render all from left to right, right could overlap on left ones
func Render(rs ...Bufferer) {
+ tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
for _, r := range rs {
buf := r.Buffer()
for _, v := range buf {
diff --git a/sparkline.go b/sparkline.go
index 44fcd08..e8e8a6b 100644
--- a/sparkline.go
+++ b/sparkline.go
@@ -26,6 +26,14 @@ func (s *Sparklines) Add(sl Sparkline) {
s.Lines = append(s.Lines, sl)
}
+// return unrenderable single sparkline, need to add it into Sparklines
+func NewSparkline() Sparkline {
+ return Sparkline{
+ Height: 1,
+ TitleColor: theme.SparklineTitle,
+ LineColor: theme.SparklineLine}
+}
+
func NewSparklines(ss ...Sparkline) *Sparklines {
s := &Sparklines{Block: *NewBlock(), Lines: ss}
return s
diff --git a/theme.go b/theme.go
new file mode 100644
index 0000000..a2294e2
--- /dev/null
+++ b/theme.go
@@ -0,0 +1,61 @@
+package termui
+
+type colorScheme struct {
+ BodyBg Attribute
+ BlockBg Attribute
+ HasBorder bool
+ BorderFg Attribute
+ BorderBg Attribute
+ BorderLabelTextFg Attribute
+ BorderLabelTextBg Attribute
+ ParTextFg Attribute
+ ParTextBg Attribute
+ SparklineLine Attribute
+ SparklineTitle Attribute
+ GaugeBar Attribute
+ GaugePercent Attribute
+ LineChartLine Attribute
+ LineChartAxes Attribute
+ ListItemFg Attribute
+ ListItemBg Attribute
+ BarChartBar Attribute
+ BarChartText Attribute
+ BarChartNum Attribute
+}
+
+// default color scheme depends on the user's terminal setting.
+var themeDefault = colorScheme{HasBorder: true}
+
+var themeHelloWorld = colorScheme{
+ BodyBg: ColorBlack,
+ BlockBg: ColorBlack,
+ HasBorder: true,
+ BorderFg: ColorWhite,
+ BorderBg: ColorBlack,
+ BorderLabelTextBg: ColorBlack,
+ BorderLabelTextFg: ColorGreen,
+ ParTextBg: ColorBlack,
+ ParTextFg: ColorWhite,
+ SparklineLine: ColorMagenta,
+ SparklineTitle: ColorWhite,
+ GaugeBar: ColorRed,
+ GaugePercent: ColorWhite,
+ LineChartLine: ColorYellow | AttrBold,
+ LineChartAxes: ColorWhite,
+ ListItemBg: ColorBlack,
+ ListItemFg: ColorYellow,
+ BarChartBar: ColorRed,
+ BarChartNum: ColorWhite,
+ BarChartText: ColorCyan,
+}
+
+var theme = themeDefault // global dep
+
+func UseTheme(th string) {
+ switch th {
+ case "helloworld":
+ theme = themeHelloWorld
+ default:
+ theme = themeDefault
+ }
+}