Add theme support

Fix some minor bugs
Add default and helloworld themes
Update README
This commit is contained in:
gizak 2015-03-11 16:15:59 -04:00
parent affd0d9c07
commit bc325c986d
14 changed files with 280 additions and 16 deletions

View File

@ -13,9 +13,9 @@ __Demo:__
## Usage ## 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 `````go
import ui "github.com/gizak/termui" // <- ui shortcut, optional 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:
<img src="./example/themedefault.gif" alt="default" type="image/tiff" width="600">
The `helloworld` color scheme drops in some colors!
<img src="./example/themehelloworld.gif" alt="helloworld" type="image/tiff" width="600">
## Widgets ## 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 ## GoDoc

12
bar.go
View File

@ -20,9 +20,9 @@ type BarChart struct {
func NewBarChart() *BarChart { func NewBarChart() *BarChart {
bc := &BarChart{Block: *NewBlock()} bc := &BarChart{Block: *NewBlock()}
bc.BarColor = ColorCyan bc.BarColor = theme.BarChartBar
bc.NumColor = ColorWhite bc.NumColor = theme.BarChartNum
bc.TextColor = ColorWhite bc.TextColor = theme.BarChartText
bc.BarGap = 1 bc.BarGap = 1
bc.BarWidth = 3 bc.BarWidth = 3
return bc return bc
@ -62,6 +62,9 @@ func (bc *BarChart) Buffer() []Point {
p := Point{} p := Point{}
p.Ch = ' ' p.Ch = ' '
p.Bg = bc.BarColor 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.X = bc.innerX + i*(bc.BarWidth+bc.BarGap) + j
p.Y = bc.innerY + bc.innerHeight - 2 - k p.Y = bc.innerY + bc.innerHeight - 2 - k
ps = append(ps, p) ps = append(ps, p)
@ -83,6 +86,9 @@ func (bc *BarChart) Buffer() []Point {
p.Ch = bc.dataNum[i][j] p.Ch = bc.dataNum[i][j]
p.Fg = bc.NumColor p.Fg = bc.NumColor
p.Bg = bc.BarColor p.Bg = bc.BarColor
if bc.BarColor == ColorDefault { // the same as above
p.Bg |= AttrReverse
}
if h == 0 { if h == 0 {
p.Bg = bc.BgColor p.Bg = bc.BgColor
} }

View File

@ -1,5 +1,6 @@
package termui package termui
// basic struct, consider it as css: display:block
type Block struct { type Block struct {
X int X int
Y int Y int
@ -22,7 +23,12 @@ type Block struct {
func NewBlock() *Block { func NewBlock() *Block {
d := Block{} d := Block{}
d.IsDisplay = true 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.Width = 2
d.Height = 2 d.Height = 2
return &d return &d

View File

@ -55,6 +55,8 @@ type LineChart struct {
func NewLineChart() *LineChart { func NewLineChart() *LineChart {
lc := &LineChart{Block: *NewBlock()} lc := &LineChart{Block: *NewBlock()}
lc.AxesColor = theme.LineChartAxes
lc.LineColor = theme.LineChartLine
lc.Mode = "braille" lc.Mode = "braille"
lc.DotStyle = '•' lc.DotStyle = '•'
lc.axisXLebelGap = 2 lc.axisXLebelGap = 2

View File

@ -13,7 +13,7 @@ func main() {
} }
defer ui.Close() defer ui.Close()
p := ui.NewP(":PRESS q TO QUIT DEMO") p := ui.NewPar(":PRESS q TO QUIT DEMO")
p.Height = 3 p.Height = 3
p.Width = 50 p.Width = 50
p.TextFgColor = ui.ColorWhite p.TextFgColor = ui.ColorWhite
@ -96,7 +96,7 @@ func main() {
lc1 := ui.NewLineChart() lc1 := ui.NewLineChart()
lc1.Border.Label = "Line Chart" lc1.Border.Label = "Line Chart"
rndwalk := (func() []float64 { rndwalk := (func() []float64 {
n := 100 n := 150
d := make([]float64, n) d := make([]float64, n)
for i := 1; i < n; i++ { for i := 1; i < n; i++ {
if i < 20 { if i < 20 {
@ -116,7 +116,7 @@ func main() {
lc1.AxesColor = ui.ColorWhite lc1.AxesColor = ui.ColorWhite
lc1.LineColor = ui.ColorYellow | ui.AttrBold 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.HasBorder = false
p1.Width = 26 p1.Width = 26
p1.Height = 2 p1.Height = 2

144
example/theme.go Normal file
View File

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

BIN
example/themedefault.tiff Normal file

Binary file not shown.

Binary file not shown.

View File

@ -10,7 +10,10 @@ type Gauge struct {
} }
func NewGauge() *Gauge { 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.Width = 12
g.Height = 5 g.Height = 5
return g return g
@ -34,6 +37,9 @@ func (g *Gauge) Buffer() []Point {
p.Y = g.innerY + i p.Y = g.innerY + i
p.Ch = ' ' p.Ch = ' '
p.Bg = g.BarColor p.Bg = g.BarColor
if p.Bg == ColorDefault {
p.Bg |= AttrReverse
}
ps = append(ps, p) ps = append(ps, p)
} }
} }
@ -47,6 +53,10 @@ func (g *Gauge) Buffer() []Point {
p.Fg = g.PercentColor p.Fg = g.PercentColor
if w > g.innerWidth/2-1+i { if w > g.innerWidth/2-1+i {
p.Bg = g.BarColor p.Bg = g.BarColor
if p.Bg == ColorDefault {
p.Bg |= AttrReverse
}
} else { } else {
p.Bg = g.Block.BgColor p.Bg = g.Block.BgColor
} }

View File

@ -13,6 +13,8 @@ type List struct {
func NewList() *List { func NewList() *List {
l := &List{Block: *NewBlock()} l := &List{Block: *NewBlock()}
l.Overflow = "hidden" l.Overflow = "hidden"
l.ItemFgColor = theme.ListItemFg
l.ItemBgColor = theme.ListItemBg
return l return l
} }

12
p.go
View File

@ -1,17 +1,21 @@
package termui package termui
type P struct { type Par struct {
Block Block
Text string Text string
TextFgColor Attribute TextFgColor Attribute
TextBgColor Attribute TextBgColor Attribute
} }
func NewP(s string) *P { func NewPar(s string) *Par {
return &P{Block: *NewBlock(), Text: s} 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() ps := p.Block.Buffer()
rs := str2runes(p.Text) rs := str2runes(p.Text)

View File

@ -2,6 +2,7 @@ package termui
import tm "github.com/nsf/termbox-go" import tm "github.com/nsf/termbox-go"
// all renderable components should implement this
type Bufferer interface { type Bufferer interface {
Buffer() []Point Buffer() []Point
} }
@ -14,7 +15,9 @@ func Close() {
tm.Close() tm.Close()
} }
// render all from left to right, right could overlap on left ones
func Render(rs ...Bufferer) { func Render(rs ...Bufferer) {
tm.Clear(tm.ColorDefault, toTmAttr(theme.BodyBg))
for _, r := range rs { for _, r := range rs {
buf := r.Buffer() buf := r.Buffer()
for _, v := range buf { for _, v := range buf {

View File

@ -26,6 +26,14 @@ func (s *Sparklines) Add(sl Sparkline) {
s.Lines = append(s.Lines, sl) 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 { func NewSparklines(ss ...Sparkline) *Sparklines {
s := &Sparklines{Block: *NewBlock(), Lines: ss} s := &Sparklines{Block: *NewBlock(), Lines: ss}
return s return s

61
theme.go Normal file
View File

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