From 373cd50f3234f7b60ef995a7600428a95c30c19e Mon Sep 17 00:00:00 2001 From: dhilipkumars Date: Fri, 17 Apr 2015 18:03:22 -0400 Subject: [PATCH 01/13] Implement Multi-colored/Stacked barGraph --- example/mbarchart.go | 48 +++++++++++ helper.go | 1 + mbar.go | 198 +++++++++++++++++++++++++++++++++++++++++++ theme.go | 6 ++ 4 files changed, 253 insertions(+) create mode 100644 example/mbarchart.go create mode 100644 mbar.go diff --git a/example/mbarchart.go b/example/mbarchart.go new file mode 100644 index 0000000..7729104 --- /dev/null +++ b/example/mbarchart.go @@ -0,0 +1,48 @@ +// Copyright 2015 Zack Guo . All rights reserved. +// Use of this source code is governed by a MIT license that can +// be found in the LICENSE file. + +// +build ignore + +package main + +import "github.com/gizak/termui" + +func main() { + err := termui.Init() + if err != nil { + panic(err) + } + defer termui.Close() + + termui.UseTheme("helloworld") + + bc := termui.NewMBarChart() + math := []int{45, 75, 34, 62} + english := []int{50, 45, 25, 57} + science := []int{75, 60, 15, 50} + compsci := []int{90, 95, 100, 100} + bc.Data[0] = math + bc.Data[1] = english + bc.Data[2] = science + bc.Data[3] = compsci + studentsName := []string{"Ken", "Rob", "Dennis", "Linus"} + bc.Border.Label = "Student's Marks X-Axis=Name Y-Axis=Marks[Math,English,Science,ComputerScience] in %" + bc.Width = 100 + bc.Height = 50 + bc.Y = 10 + bc.BarWidth = 10 + bc.DataLabels = studentsName + + bc.TextColor = termui.ColorGreen //this is color for label (x-axis) + bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience + bc.BarColor[1] = termui.ColorYellow //Bar Color for english + bc.NumColor[3] = termui.ColorRed // Num color for computerscience + bc.NumColor[1] = termui.ColorRed // num color for english + + //Other colors are automatically populated, btw All the students seems do well in computerscience. :p + + termui.Render(bc) + + <-termui.EventCh() +} diff --git a/helper.go b/helper.go index dec705e..80d8a02 100644 --- a/helper.go +++ b/helper.go @@ -24,6 +24,7 @@ const ( ColorWhite ) +const NumberofColors = 8 //Have a constant that defines number of colors const ( AttrBold Attribute = 1 << (iota + 9) AttrUnderline diff --git a/mbar.go b/mbar.go new file mode 100644 index 0000000..3b3c253 --- /dev/null +++ b/mbar.go @@ -0,0 +1,198 @@ +// Copyright 2015 Zack Guo . 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 ( + "fmt" +) + +// This is the implemetation of multi-colored or stacked bar graph. This is different from default barGraph which is implemented in bar.go +// Multi-Colored-BarChart creates multiple bars in a widget: +/* + bc := termui.NewMBarChart() + data := make([][]int, 2) + data[0] := []int{3, 2, 5, 7, 9, 4} + data[1] := []int{7, 8, 5, 3, 1, 6} + bclabels := []string{"S0", "S1", "S2", "S3", "S4", "S5"} + bc.Border.Label = "Bar Chart" + bc.Data = data + bc.Width = 26 + bc.Height = 10 + bc.DataLabels = bclabels + bc.TextColor = termui.ColorGreen + bc.BarColor = termui.ColorRed + bc.NumColor = termui.ColorYellow +*/ +type MBarChart struct { + Block + BarColor [NumberofColors]Attribute + TextColor Attribute + NumColor [NumberofColors]Attribute + Data [NumberofColors][]int + DataLabels []string + BarWidth int + BarGap int + labels [][]rune + dataNum [NumberofColors][][]rune + numBar int + scale float64 + max int + minDataLen int + numStack int +} + +// NewBarChart returns a new *BarChart with current theme. +func NewMBarChart() *MBarChart { + bc := &MBarChart{Block: *NewBlock()} + bc.BarColor[0] = theme.MBarChartBar + bc.NumColor[0] = theme.MBarChartNum + bc.TextColor = theme.MBarChartText + bc.BarGap = 1 + bc.BarWidth = 3 + return bc +} + +func (bc *MBarChart) layout() { + bc.numBar = bc.innerWidth / (bc.BarGap + bc.BarWidth) + bc.labels = make([][]rune, bc.numBar) + DataLen := 0 + LabelLen := len(bc.DataLabels) + bc.minDataLen = 9999 //Set this to some very hight value so that we find the minimum one We want to know which array among data[][] has got the least lenght + + // We need to know how many stack/data array data[0] , data[1] are there + for i := 0; i < len(bc.Data); i++ { + if bc.Data[i] == nil { + break + } + DataLen++ + } + bc.numStack = DataLen + + //We need to what is the mimimum size of data array data[0] could have 10 elements data[1] could have only 5, so we plot only 5 bar graphs + + for i := 0; i < DataLen; i++ { + if bc.minDataLen > len(bc.Data[i]) { + bc.minDataLen = len(bc.Data[i]) + } + } + + if LabelLen > bc.minDataLen { + LabelLen = bc.minDataLen + } + + for i := 0; i < LabelLen; i++ { + bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) + } + + for i := 0; i < bc.numStack; i++ { + bc.dataNum[i] = make([][]rune, len(bc.Data[i])) + //For each stack of bar calcualte the rune + for j := 0; j < LabelLen; j++ { + n := bc.Data[i][j] + s := fmt.Sprint(n) + bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth) + } + //If color is not defined by default then populate a color that is different from the prevous bar + if bc.BarColor[i] == ColorDefault && bc.NumColor[i] == ColorDefault { + if i == 0 { + bc.BarColor[i] = ColorBlack + } else { + bc.BarColor[i] = bc.BarColor[i-1] + 1 + if bc.BarColor[i] > NumberofColors { + bc.BarColor[i] = ColorBlack + } + } + bc.NumColor[i] = (NumberofColors + 1) - bc.BarColor[i] //Make NumColor opposite of barColor for visibility + } + } + + //If Max value is not set then we have to populate, this time the max value will be max(sum(d1[0],d2[0],d3[0]) .... sum(d1[n], d2[n], d3[n])) + + if bc.max == 0 { + bc.max = -1 + } + for i := 0; i < bc.minDataLen && i < LabelLen; i++ { + var dsum int + for j := 0; j < bc.numStack; j++ { + dsum += bc.Data[j][i] + } + if dsum > bc.max { + bc.max = dsum + } + } + bc.scale = float64(bc.max) / float64(bc.innerHeight-1) +} + +func (bc *MBarChart) SetMax(max int) { + + if max > 0 { + bc.max = max + } +} + +// Buffer implements Bufferer interface. +func (bc *MBarChart) Buffer() []Point { + ps := bc.Block.Buffer() + bc.layout() + var oftX int + + for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ { + ph := 0 //Previous Height to stack up + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + oftX = i * (bc.BarWidth + bc.BarGap) + // plot bars + for j := 0; j < bc.BarWidth; j++ { + for k := 0; k < h; k++ { + p := Point{} + p.Ch = ' ' + p.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 + p.Y = bc.innerY + bc.innerHeight - 2 - k - ph + ps = append(ps, p) + } + } + ph += h + } + // plot text + for j, k := 0, 0; j < len(bc.labels[i]); j++ { + w := charWidth(bc.labels[i][j]) + p := Point{} + p.Ch = bc.labels[i][j] + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + bc.innerHeight - 1 + p.X = bc.innerX + oftX + k + ps = append(ps, p) + k += w + } + // plot num + ph = 0 //re-initialize previous height + for i1 := 0; i1 < bc.numStack; i1++ { + h := int(float64(bc.Data[i1][i]) / bc.scale) + for j := 0; j < len(bc.dataNum[i1][i]); j++ { + p := Point{} + p.Ch = bc.dataNum[i1][i][j] + p.Fg = bc.NumColor[i1] + p.Bg = bc.BarColor[i1] + if bc.BarColor[i1] == ColorDefault { // the same as above + p.Bg |= AttrReverse + } + if h == 0 { + p.Bg = bc.BgColor + } + p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j + p.Y = bc.innerY + bc.innerHeight - 2 - ph + ps = append(ps, p) + } + ph += h + } + } + + return bc.Block.chopOverflow(ps) +} diff --git a/theme.go b/theme.go index 092d06b..c8ad947 100644 --- a/theme.go +++ b/theme.go @@ -26,6 +26,9 @@ type ColorScheme struct { BarChartBar Attribute BarChartText Attribute BarChartNum Attribute + MBarChartBar Attribute + MBarChartText Attribute + MBarChartNum Attribute } // default color scheme depends on the user's terminal setting. @@ -52,6 +55,9 @@ var themeHelloWorld = ColorScheme{ BarChartBar: ColorRed, BarChartNum: ColorWhite, BarChartText: ColorCyan, + MBarChartBar: ColorRed, + MBarChartNum: ColorWhite, + MBarChartText: ColorCyan, } var theme = themeDefault // global dep From d4551211f30c93296540618a155484b34bb3e3c5 Mon Sep 17 00:00:00 2001 From: dhilipkumars Date: Sat, 18 Apr 2015 00:38:59 -0400 Subject: [PATCH 02/13] Do not print the label if the value is 0 and fix some OutofIndex Panic error on very large dataset --- mbar.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mbar.go b/mbar.go index 3b3c253..2f825a2 100644 --- a/mbar.go +++ b/mbar.go @@ -82,14 +82,14 @@ func (bc *MBarChart) layout() { LabelLen = bc.minDataLen } - for i := 0; i < LabelLen; i++ { + for i := 0; i < LabelLen && i < bc.numBar; i++ { bc.labels[i] = trimStr2Runes(bc.DataLabels[i], bc.BarWidth) } for i := 0; i < bc.numStack; i++ { bc.dataNum[i] = make([][]rune, len(bc.Data[i])) //For each stack of bar calcualte the rune - for j := 0; j < LabelLen; j++ { + for j := 0; j < LabelLen && i < bc.numBar; j++ { n := bc.Data[i][j] s := fmt.Sprint(n) bc.dataNum[i][j] = trimStr2Runes(s, bc.BarWidth) @@ -175,7 +175,7 @@ func (bc *MBarChart) Buffer() []Point { ph = 0 //re-initialize previous height for i1 := 0; i1 < bc.numStack; i1++ { h := int(float64(bc.Data[i1][i]) / bc.scale) - for j := 0; j < len(bc.dataNum[i1][i]); j++ { + for j := 0; j < len(bc.dataNum[i1][i]) && h > 0; j++ { p := Point{} p.Ch = bc.dataNum[i1][i][j] p.Fg = bc.NumColor[i1] @@ -186,7 +186,7 @@ func (bc *MBarChart) Buffer() []Point { if h == 0 { p.Bg = bc.BgColor } - p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i]))/2 + j + p.X = bc.innerX + oftX + (bc.BarWidth-len(bc.dataNum[i1][i]))/2 + j p.Y = bc.innerY + bc.innerHeight - 2 - ph ps = append(ps, p) } From 5aa48ab0f3d7eb54be7e8a3e41469f56824451ef Mon Sep 17 00:00:00 2001 From: NHOrus Date: Mon, 20 Apr 2015 10:41:55 +0300 Subject: [PATCH 03/13] Decoupled interface drawing from value updating for more fluidity on resize. goes into gizak/termui#32 , but does not fix the bug, just prettifies visuals of demo --- example/grid.go | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/example/grid.go b/example/grid.go index c010b4b..9dbbc11 100644 --- a/example/grid.go +++ b/example/grid.go @@ -92,34 +92,39 @@ func main() { // calculate layout ui.Body.Align() - draw := func(t int) { - sp.Lines[0].Data = spdata[t:] - lc.Data = sinps[2*t:] - ui.Render(ui.Body) + done := make(chan bool) + + draw := func() { + for i := 0; i < 103; i++ { + for _, g := range gs { + g.Percent = (g.Percent + 3) % 100 + } + + sp.Lines[0].Data = spdata[i:] + lc.Data = sinps[2*i:] + + time.Sleep(time.Second / 2) + } + done <- true } evt := ui.EventCh() - i := 0 + go draw() for { select { case e := <-evt: if e.Type == ui.EventKey && e.Ch == 'q' { return } + if e.Type == ui.EventResize { + ui.Body.Width = ui.TermWidth() + ui.Body.Align() + } + case <-done: + return default: - for _, g := range gs { - g.Percent = (g.Percent + 3) % 100 - } - ui.Body.Width = ui.TermWidth() - ui.Body.Align() - - draw(i) - i++ - if i == 102 { - return - } - time.Sleep(time.Second / 2) + ui.Render(ui.Body) } } } From 3968b024327a545f02d0177c25e7ac16df34d16b Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Tue, 21 Apr 2015 16:43:09 +0200 Subject: [PATCH 04/13] Added support for gauges with custom labels. --- example/gauge.go | 10 +++++++++- gauge.go | 26 ++++++++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/example/gauge.go b/example/gauge.go index 06e4946..45cc29d 100644 --- a/example/gauge.go +++ b/example/gauge.go @@ -47,7 +47,15 @@ func main() { g1.Border.FgColor = termui.ColorWhite g1.Border.LabelFgColor = termui.ColorMagenta - termui.Render(g0, g1, g2) + g3 := termui.NewGauge() + g3.Percent = 50 + g3.Width = 50 + g3.Height = 3 + g3.Y = 11 + g3.Border.Label = "Gauge with custom label" + g3.Label = "{{percent}}% (100MBs free)" + + termui.Render(g0, g1, g2, g3) <-termui.EventCh() } diff --git a/gauge.go b/gauge.go index 3e0003d..d5abbcd 100644 --- a/gauge.go +++ b/gauge.go @@ -4,7 +4,10 @@ package termui -import "strconv" +import ( + "strconv" + "strings" +) // Gauge is a progress bar like widget. // A simple example: @@ -22,6 +25,7 @@ type Gauge struct { Percent int BarColor Attribute PercentColor Attribute + Label string } // NewGauge return a new gauge with current theme. @@ -29,7 +33,10 @@ func NewGauge() *Gauge { g := &Gauge{ Block: *NewBlock(), PercentColor: theme.GaugePercent, - BarColor: theme.GaugeBar} + BarColor: theme.GaugeBar, + Label: "{{percent}}%", + } + g.Width = 12 g.Height = 5 return g @@ -39,14 +46,8 @@ func NewGauge() *Gauge { 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 + w := g.Percent * g.innerWidth / 100 for i := 0; i < g.innerHeight; i++ { for j := 0; j < w; j++ { p := Point{} @@ -62,13 +63,17 @@ func (g *Gauge) Buffer() []Point { } // plot percentage + s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) + prx := g.innerX + (g.innerWidth-strWidth(s))/2 + pry := g.innerY + g.innerHeight/2 + rs := str2runes(s) 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 { + if w > (g.innerWidth-strWidth(s))/2+i { p.Bg = g.BarColor if p.Bg == ColorDefault { p.Bg |= AttrReverse @@ -77,6 +82,7 @@ func (g *Gauge) Buffer() []Point { } else { p.Bg = g.Block.BgColor } + ps = append(ps, p) } return g.Block.chopOverflow(ps) From 137d2a7eb2c6adc5f129e04fa829b4f0ccf42605 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Tue, 21 Apr 2015 17:41:52 +0200 Subject: [PATCH 05/13] Added support for label align in `gauge`s. --- README.md | 2 +- example/gauge.go | 1 + example/gauge.png | Bin 29794 -> 32431 bytes gauge.go | 30 +++++++++++++++++++++++++++--- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 87e7a90..7cdaf27 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# termui [![Build Status](https://travis-ci.org/gizak/termui.svg)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui) +# termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui) --- diff --git a/example/gauge.go b/example/gauge.go index 45cc29d..a05a2d9 100644 --- a/example/gauge.go +++ b/example/gauge.go @@ -54,6 +54,7 @@ func main() { g3.Y = 11 g3.Border.Label = "Gauge with custom label" g3.Label = "{{percent}}% (100MBs free)" + g3.Align = termui.AlignRight termui.Render(g0, g1, g2, g3) diff --git a/example/gauge.png b/example/gauge.png index 17eb3ea03ed564c2fd5ec522efd2514bb415389f..5c20e6e8ae37be1c08392a294b8fdc64db6bc216 100644 GIT binary patch literal 32431 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 Ef=cVEssI20 literal 29794 zcmZ^Jb983Q6KyoH@x>EkVohvoV%x^Vwrx8T+qP}n6K7&4FLUSKckl15_15a|`{S%$ z=X6(9@7lGiLu8~x;GnUffq;PE#6$(CU1oal=l{(m~sm0F%0#Y`H@pl12WY5v`vdZKgvnHT~$>9YGHlQyQSCtAMb{}%XZ>^nG!5M0gCNnlP6i6)OXBK;- zkv{I+m&_a**2|Zws-unNzuVu3W>CF;pWJ@fB%zdNdV}XuDK_ot61BUyz}McfA%4^ANF#*~$GpZf`1#aD4+;y!!xFTQ4T2d-GJ=NwTDCqW(( z@MeNROH1w=9v842qyISVZdIW@$69FP$3JLWv11$4j*(Iy;5($#mLAF2lr} zo0b{224`<#Exah2{ypfEDYxz}H@|G2N-gkS;}`yS(PD&x)C7(V+OgR2SQD9m)089t zE_p=%PIL2R&h84UhBi+zczEyrV7TeP*6CFwP8c<^fpGYverr<-aLkO&K;*%JNW`Gy z&u%DET!>Ryc1l+M559Wb{3$J{JkKW_-MrPDzz~WteC})A1xh1MkA<+V2i)xK8~7 z+w&+&*lSQupQH3gqB;`}MQ%vIkzYd!P}rAb97xOfyPl<28%2BM`O&wLwn=l6zC-wT zDbbwPst|bP8dqAx7I>fV7(1m70) zau`zH6RB^x#CFQC;6D#a>Ig9JTB)AZ%S?unCT7%qv5Y~s#H2&8UK=b~G3z?M_G-)c zTEI*GxPc$VK`Y&c?_TriI-qT?jn+4qY448)R>LLEk*DHsKSt zMR5qm(#^%n^3)AO2e#pZ1jZ){3711;77VW!oJ_bUVR1&QnBR|Bp$1oGH#`hEry!Z7As$YhpB-|`V_z-mt>Xu(%%ETU!8hViZ z=@8nUS}jm1J7}8y2FDexJ>)Kzc1jmkrv?Lbwx5cQYzzWs5V;yvGMIWGv|6qVq_i7r zyRjqkR?lxqBt3Z6hSUo9r!oF#zuHS2clr+S%}C`P2WLu8+>73zUzK23d~rHb-wD8w zcA$#=oBav-zib0-6K{iP$iDhJ2@=NeF_E(O)Ti)DlF{H-5S$Y)Ag6_B_6_#wB8TJ& zGmtgM^oUW(eJ&2Ji?C0&Pn`R9Oy+?<8tX8`Y~W=}m?BL>oD_=?Ga21FJUK+f0L>7{ zK*O-jfRpmbaAI&b$~2-8ca(&gLoOHo#VX!JoZS#JKU_|rnYUTmg@}@}l*|y z0{H@KfsHcCJkxCWoJMxDbgibHiiA2PuS9nJx0AMoY-WK~c@9mt+B?iMVBD6-R(NUS z+6mPRo)ij6S|ifh@Z}MlnfW=mIkcnm`Lik9X~u=HY0A0MdADroX{QCLjuTpojL zeY$a-9KQN)O#w^+vR|%W3&332B;)BICGYE(aXrt)wWV9MT8U70GM*eD%L-h8l|+4r>im z0@VO&3+f%|dUPZWv>n^GR)$%GT@1u82!<+ z8&lXpHhAs*laW@w(!R1+i?+SIEntJJee=2Lf$GZiuHPNhJb_JxeMkI^+VSOu&)-H+s?T~hWR|EPN!km`6t|hKhC1}~SF&T7xmJ^lLx<^m zJxM)5dg|({YDWE1z0JYL0n6}{h%Dqz@;5VIR}k79f|z)WG-rGp=fKTyYQ`tyl}V~p z7xTo+GM8GKNu{g|w&xq1N47Gy!8k}U8?m5R?%Ezzw6g8J>^hW#>{Fm`4$P7!4}SRjH+7tsPpsUwyuM#qqXb zUOSAfUs=3NTwH>?Qn#}Cs`hr$kSD6*o9b%ygzkF9ZQt$7Z6mX@MbTPOQ?<_S?zdUy zc}pWJnvEC7(RAu0i8FC0>wF6|%QI>iYV#$dD$Cb~$<^70O{#mE%JrOP{oBZU4mOTf zCztb*golbmZVefOdQ5klh|YQmPovfmZ#Zzi?lJlh?p_Ebh~3U|Mf z!AhU9it(sw1kH_CVwXslURa8n9I~Uim*h{^U+!5acH&yhSW@tm+q$k}j-;R&3r_;l zajgJM2$>RG_f3XUqwUw1KdtMetk0SdZMSEH-ot*bwyjKP*|yHvFYjL!Q%WfxSF&rA zn>SkXJhr}Gka6I!A6KwxnIGDpTb!n@I25e@cwTeZJVCbxTW@k@JT+P@PgZYqGrGIk zI9T)aB)gs{ZGChSIj3%yZqk0?-R3(I7!N}8N`8}FJ(+TAnQW|lm7V+H7PTCu%{lCG z)$G+TzcM>x%syF?xyosc^$YLt#cm+$l@^Rv7(DDv?!|miA})#S?e3-3gWR*|J>l2< z;mKF)xeXk5^ZN!vR=tPq)-orei!h!dkD`~uo9Z+9wL)VeJDxc1BoF%6nTM}?C$H0G z)3U%4L$Ma0=v1cTQGvAZz`PTI?%{voh{VKYb`pPz$;2c9dke`oW)iaj+R7#aqL%=M z3~-Crtis|i=xKF+#yx|QQC|&VSF)A2u*_>N`h_a+K*#G^<7le4hRT9^7{uYCP(}W z2#6O*Ops5(8Td2GC&K(a9Rs#AseoQST~X$*+v!nML&2~RGQkofK2V%o+ za6D(y-JTvd%`exSHtZtv%gd=hfg|Gs|98lPD9tL6#sWb_|7YYu$M-;CY`)0r3g6>!(}dcngt#mba~4L};Y z#KCrk7E_k;tqycqI}_`V)HG&i*iS6XMBO&i_)Dw`lT@yPy**Qu=PXz z%Fg8*2r=(J<9jk>c&ko>)-~4+)0f&|^wAYqOAPzQu1?7c?S&cdz6E0$YjbZBB#{|L z^mRgtQ5e1YasT-F0--41NmEhJJJ%bRUBZKLfhpCfP?SU<)UgBaWbuDq&I=Oc)71oh zF!yCKTf47@6!KM|b<4#DCD)CxszHj+c7gCl+LX6Pa;W14zbDl@(-kdNnYNqUa+IRO z&pPKI8v&pWU2&%VZJh@m^rtV4T-F;Im=KkDPR0GVr7UNx>S*kRPos29Ucmh4s8qxL zO8!~@Zj2X1#QWphMx(=aLR4h4vnAL|-a`l%GnxnuR69W*K{E7uZVr6>To^6)d9-y>x(+2oMyu15t0A- z7F{tUjxRE9ru<9=Qzg|=SnkdvPcZ(R#rQyrI%0>Ng@--v4kK>Xxj#w%eNvsu_UbJw z_=Wq(Qgh!T{$sOK7vbByqs=>{b4lM$Ici9*;p!}8b7G-K_iXPu^i!8O@5+?}X9(|G&yb-qq2q>Fu!6}v@{ZF6@T9gA zk@FQH`EiHb5;*h{$i%&7RChCV@KGjx6%1xFws9_tB8cI!dI0u zh7rXHL_SgcC7~CDkjav?CxJ}@a`u(I;MKn$?K*(S=;Y zQ9Fz^wWYZsIVt|^X%ZpDQJdRLY77ijp)U@|Efef;4W~O|>$IGv4)|MEygJKnQ)>!A zU9|7HjV%^5;Oz-VE(*a@G>~xAkOmm6*rj$gmZcSZn;gd2805>Ln=X{#o6D)x=CnF) z$U9lj-gTv(z0uUfI7P)l|Fx#?ALUsQ7&V=L{b*{aO?YD8+LUlG<2JA0Se0Rj#KjfiBn=_K~|0JI=TE+z*HA7fF0{Q!q* z!naX`ma&A1&7nr;s4`7s!r+xmE|ax8=;OwAI49Ah+h=v9oE3Z{q);?X+m!S1uCCO% zgW|pP$hX=9!~UADY~BTp+Pv!%8@_i}%cR`T1TLo2{kE1DG)x_L6eX@b%TSKbNnNj( z^ULPKe|zD>7h5L8bRAE(&6c8?*{WpgdWssK)~Y}X|fzy zM(TBE^W0zszCE*ir+S9)r4W74O5eddGK70F~}BcZXmF2vsk`A{QR|cY;$3> zzsE|klcb6iA^TE!Q$(k7C0Utj(g$R;HZ@7jftr8$Qv|T2%bgT#nHv$8R=(goN2Sna z$&qOyZaG;l)Cql_(!TY-$j*8U8D6T>VDZ9cX5@j?4r5lUBz|coRK@=oQ~8!=l%w~E zS>*Bn4{OEAWTPB^N)CW!??uWx%fbvRBQ&JsUpG_J!Ebk$MJk|gEHrWIHEKFj1uPgK z>3+wj-Am?SG&BfO%CQ(Y$LV$!4l!--RQqNLiK`+kYTQ?c`V zz45SaxSE$)%C;wm`!|!T!0GAftlLDqipbbW*Ms<>0F4{3gafMZVt1`@rW@AgL#l9k zTl$%)-M!pex`BbPFim^&!2t-H#Xv`WC!)|KvSj6SAI2nFG{kRK@R8c{>Bp`U8Rqs?9;V7?h2f)c6a7(`71~;v`$^F(|SnvKOQ#+Aw{sE z^GPgMpe@P+JW}Rn%f?bT({LavV8#R^!5ZFn8>bg{hE61Ri^2@|yOIQ8GV3rFSEke# zI1?se znv+rJr1!7+X%4`~7b34z4=wemBs1Oq&1n%-5xQKg0F~j4q|Duom4&8 zKif+Q)CUj7KGPRUe2&zHFeJ&gBEg;g+}Hu7zN26#al5d()ya28?eZD{`D9nV+~DK^ z{0d&Jfpik|&I>`8EZ&&(Ym862)a-IxZ6M(Q7GPI@d-;M32y0YaQ=UtTDB!9jK&m@X z9fx}zUDeuYVg#-vh>-nH*g=2{%X+4j!{uQru&f9O9 z!oI#}?O9~9+={ayUFMzo_E`L2Q#xD2<(*?NS~$!uyGPkJSK?f)QWY#t8kvLXy6Y-e zeK)V25ns75vf`IcV`!8GHzqlL{bKL1FN4v6w7_UA`2R8Vl?&88!JIA4MyHY5lML%M ze2_GkK{w91^%d0wL8oZoGB$ClO5Y95AU6#OEb<<^g+jWd~vA)mHIQid8yEMhpt(mP6LWfM3e3Y9i^P1 z0RU^zPHxzdc0K5v|{C%d8Oa$Q|We!S!SgL55UoUhB`F<6v6`Q zuAw(Z23J=g61*w2&RA^`|1c78V!u;NodVkH!`pB3%kyQ;HwXK^ie?Da7(+t8L;2F> zFso8@dxpTMJdiPHlga;QsTKX>SKQ}YB1QNKF6uMCOlrn=3fQ@WFvG8l&7CajWp>+U zsO$0uZ&=O?6=R8p{{LBYW*1#pvGCM3fqJsUZA?jpaHsa%^8BXIc$X)uQ$100DasdR zT;wn*LFqUZd`7qj0XMp;dKL4uEocvwyF10J(y8ld+HBWnZlnwb_tsO>wrv)Snd1o_ z-1C$XM-%a2!XxvcJESrTAE$Y>JWdK$iSHb~8D_)1Pi`mFzwlmwLEuVO`#qP%a_^ql zs(9M@_B_z2?4N~b%^bN*8-<}EnRMqBf|M|s#;mpVKOzkFvP((ppU#MUj%55&R zNG~<%?wQ9O_a&-KfQcPp+fHeikn1d;q|tR};%D^B%5v2RJUYEOAU^|U!hw@FAIcZzvf1-KO;zU#jl~n{Wxy<`0xf zhc2~bQ)?3Hl@9tauT!v-(3rWD9xp^YFD9@aE=tGAo0*q`XVfCa_cGV%e4F4O&jMX` zr*2;=oQZ4}q*D4J_#|yDxPQB-J5(8Z@VPTw0^ViXZy~Px>TQowRI7&Wj2o$zIa`0< zuuHsLn7+Df?;_C696fh*j+V$*PY+(}mu$IS$Rvbp`!?%MWqRuVMzw`QK^rr7?Qz?K^yLxY+3ab#M* zgoCx9(h@Evz$FlL0ehRfe7Z1kZ!Wd~0St4}hSQtw#w^@HG z8SllS)reNk3qu}m)qULN`EhX8kxRHnrgVSs{I1;C*69vombpSI;%-h$=z6_iK%3gw z77{0eaduWm^D}z)(?3O6*R*ig%))|_#xyGS*^c?IS7~Yq4rzq; zU*;NK*Npw*Mzgqu>7UYYMsyvFUqtzBm-^Jb_yogyVPRx0D}<_siv1J)+gSbKq0@pS zYHZ5eQJJ%L^a;cpoM?Tc-W&`tk}@Ff*T#m!{GJgt8#lK0HCv#bUp^i0baep)??WMm z_E~gvVp<~B=cv7S-!rhsv1Qf_0At=l9g|gkyE~;DA%f3OvDb{&!NvblS2}SJ!^4G_ z&jZUO0v9w5mzRWvAjgKJ))p{5Uds$6M{4UnNHb!2Rd!#NhLGyAv1TNq9J(#ReqWAy$xUm=sZomuWbm zJc;b@*ie1T)F=73@P8c0KoreJ_38V;()&i9ZQ+xlb)&2pr}dC@{BG_#t(C1{c<`Us zgW=0$>U=&GAlCJp1PBppH7Ta_>|@{M{%rjQWD#c%z3Md5mw-ny^h$kkBwKpT0tM+) zIq0|dk41JRbii61ntFCTaQ_oRf589_CWwBv`Y6hn9_kE& z6*q&Oapk`b`*;~0l@I+?`99QvuE@Z@W%KVTbo7uibkgC^L#qFkF~U)RX;!hwsw@(J zLv$b!o!zD0!%P79<#Fj5Nm!Q4_U3le6EQq^hRH9ms>jPseSsqil2v`uzZTUs^dW*o zrK3sw)<(LLkyy`C@wR$L@t$w!<1NW@zBD%rBeqv?7}m#00Ru&lFSIyxDp6 z!8LMb#8qgFY1kks(VF@SL{?lZKeE3)Obz%lNdA#aFxY^h4`OQd#C|&-0~>^qCp-*J zPt|nJeK4o~VMKZ(%rvq|tn+hSx+~gSZ)VR=YfsX&_YK-je{98k_oPR*tNH1 zyqyq>FMpRc!UEphV?T2GYyL6`I2iZSFrzG&uVckaqAV4H-k`o$SEc8(mvj4OY`JFr z3!%GvwQI3TxkELMkiy-lG{mO6``#+v@Ph82l%`|UG#p-^1^wFp`B;F%rMWtf=+jAqv$Ocu-=w_cc!j* z-5b1|t59j#7G1w=A|)!jcH8;-mz8-!HKYWW=DAzjC+J+Ko&7VTLxVIK4gvE@5_+PM+AF>PIE@Gqav1={-x-=}4c&cMM8H zb4LEZf`bm{!^GQ=^jUwsI=JiEx(S1NA-*oL3;X^B@!0}12`cU`iN<82K)B^Ths!R3 z{hCpQk?UUCsZTR#pD>3qU3_(>rIlVDE19`AUR=v(l!k*$JrU{8QMCinxG|mx_zO|! z3c%#hJ)ug~<{0J9GA4I;GV8WR>};Jn=p2)4j$A<9HYN(XKYQCn?7 z=k`Kwr{C7>cO{x$$S#J=54m4X3Ri6AjW_*vPtam4{nj&iIjuxaKH&}8m^wv=uL>Um zIli3B0sl);p-Ti)O7~e#IOZNgTwZhC zaQd|lbAwBB>e*hKislt%J<2_Ni5s9iTGUASWV$!DRw@9XVpX*ryQfN>!?G& zpmilxx2yncB^F3ubwSN}KNa;+DZ<$FPSOhcb(4S+7Prqz_bYdz4Gwm&vuBz0Zeje+ z2|i+cA2~;E%}6Ttw=9CcRbD!Z9Y|r7(AV?>+95Y=opFwOn+p7-R;k$THLK>CQvZ^F z&iEyldf4-H^%sS!)ak(KC!4m{nd^sL>U#s~!KA{*TKOmF+~@t!VrUqFq695&Anah8<*=;y zFrrB9?5vJcsNwqQpowYokh|@n$PuwYYZU`VNKy84iK@?q2Xn|X^MK8+=XR7bn2x)llT+DUmhQhxdk2N38f7VkU3Cm;({fU~IkGxb%jzRD{c7G&w z#oe#_kVS25+Zg;l(R4Xb$sUs|Ib>tQ|1y~0Dct0PB18oOM*rLEkYxWzKXK=Yc0GT< z;LraL0^V*;EHx^L|Bwv2Bgpv1L+dJp7=Ji!b<`N=CDfJ0UA|fS|LvW0=@QX_th2AF zjMUPxDE8{hGfy;j8&+aeARNT~#c}@TCtWcFbl@%ZoZVCd`mgE5M2t>%|LFvLETb10 z19V;OgLOF?Q2oQB{;Uqg59rf%UsF{2|K;DmcgKqK0b~DAoj+z0)zL%D*2%=UkE#7b zI{y4)kbUfV7c6o7e+&Wp@epN~+ZOO&L+lEE07H6Ljq0DA^C!YWexrrnn#hFjU!MF3 zmXGm~5(w}a z;qpJ8!lCfVLHCR}vGz2Y8;?JVMe?(p)2s&h-#Kg``^Sq{RAUU2LdKaPU(nDF7#)>z zH?sJZm8Up6RVYqU;t0@f>B)35M}7uqj6o$AUbzsU+kqT>hE15KviEJlt-l(` zF3nmWzf0sNQYTA%9pU_s&4H1V$-zeKhm*-?e{%lq+&^~DX8M4c8TaJ>G5hcbs!Cr$ zc+dWY4}ZUh{F}FLGZWsy|9IW@clf^mZ{7UY7vdiok!C!o`M;46m;MoS#9lzG9{*XE z&&=gF1V@Y)CyDXRS!_1kdV5xywH}cl>D0fkFsQS31y<@@opAuGLBr zaSyvDKz;1(A{Y;HR~(oyH}nr(?I2udeq8GupB5L@B@sx;BiXmrql{3se`BP}A4#yf zl<_1X0(Cb332fTqcVwq7Yr|TFJN%sFQe877k$AGZmB zaF3(#9riRyk06A^#}_k#=|3Wk!#_tD8kW-MInHO=*l zt44HnbDdtV>`=(xZf!FuD;G$ZRJ_3ssX315ei?CPRSEg$=w_4qAVD8sJl!J{Ma(Jm zYG!ItvIi3-#wbRMSb+69S~w_lQRXuh8=)t~TtXKM5`XCQce6Bl3o%>ryF5flQ?r&n}nm~g*R&FFL`DLq8nfo6x%hp z`S()!BY!hr;_qWf7(O~udPR*A8Oeo62}atpg~-9eyP_dmwrsdpJg;DcIf%JXLaAz7 z-(3U`o444N(+Q3hn$03c-sjx(Ah|Jk=k(%S8R89aw!dT07aO-Xfmbs=ZHpcYNUFq| z999VK8K+9~L05}{#h6G=W6mVNNr_ZEuC`PGLMZ##t;Dh&8zH(^1)WQ3hu<8VD2OXW zzaYuD+|~vNV~P#vAGxUPQQp1KW2VWCvdP#K!%V2jT0mW5g%m_DA@^-bRNKo6O>t<_ zsHTyJ(HP!5woEK#0;EY$g8OqZlR#DD$*yRwaeb7CUsJ^U5YZYxy2fNZITl(fO|Jfh z4H$C6RVx|aQBm|mh2x$-Cq7rKb;hqbDF;AQ(C?(+CRbs+zDnp8xp^s3J!7j+%ba~N zAzKw8MaDiJWhgaG2>{RHTowPNq`v}7=@^!*q~X@$8YFXeXM7>id^=;A^pyu`6cnP$ zuJZ|2I%OHw+UH5SapG@g{((P+h*R?>F(buGh{(7BGUp zy$3$y@R6J+#GB?rQp*THe7?V!fR-#TDfMJGnxmjaf9!_tAAH^FSbMIBFEPvXA$`xG zr5n3bnRt!+RNBr}ve;7UNRcW;g@A{QalCjs?NBt`XIqe~s0D=fm~QnIpVkA4hhj#& zW6d=sa5N>WEz^M6B}N!zY4HtkTsXmRX(+s9oO%H94>fCZwBZ3BN}Qs~yc@#;$5 zg4Od1qUqrLtcvLH4Xurgd_6gM|3g|FSF-==h`*FW+_ZJSLia0`YOE$q2DM#dlNr66 zO_*l}>?vGtS>10y9J|Lu6N9ee0HxF-U$1$>UqP*Ebt_Jj(EXcQ$&hsto~5NH*LJkN zlEOpZH7mI_RzFqE4hUlBhA%;-ic{E)aj{KTr;_8P^G!Czm|!j%Whrxxirl@Pb)zRj z+Z%;S96c=94U@i;QW~KV3Zg=Au;Ma?hIlT3xH9+f90&Apsc^Yc^Vi5LjKR~%W+zI* zxyvth@hFWGqX+4eq6=**oG}^|%d+w@#~9=U)j3>|jh%(G|M zN>*V1T_~in+~m>nBumemYx$1;;v%yisvsDSJ963zz1xje(CGnFlafUAd5hKXzv&wg zRJ=e}5h(OY7_E`Du)E7~=@!=Ads}E1^7OuC-&g?!t5mu0JCs}P87acj)Rhq&vYT90 z!wO;+7VlzwHl$L$uJSh+wG$812vw&l7!E5%7WvwARKF8O(^lJ-^1PZY3L>L;_E~KA z67r%FU$=Kh98q%?S|iCU5M`BPhxvl?RWxf*0hx828b7SU;Q7?*^H2GM1_{Sx*ys|r zOu(g(*n`Gvgoy}7ZjGllIX3bn$OJHDFgTEPa0G{+1rr*fKR-UbkKf!`F7Fh?#H&2j z>MhF$VN%ztdM%Jv*EkL}c&E_9)ZF5BfY&|FjT+?f)PBQ}S-5jm98X?949YFl7g%4a zZsb`1&ihi?&R|R}2+=wIOfu^Bss*>Y?dq1?QGX_ZQVMm3hz8_6w>s~XO zFEonJ?aSPK=`dcFagaua)>}aQgi@Mrexh%Z)DfE-oY+qyYS#VF+L)t?Z%+ewP zSNv4@0A7wjF+6kK+8H+OO8_yib?xgMz*3vQ6b{2p;+WnSFxRh?Z4k3+$AVNR`|+Tv zOu$8jiJ@rk!d+#Fr6r$cQ|)a|emU>IIh5W@s~e^iCyVu0T#WGqf67~EB#M6j zV}KOlDW5Cf!7|36`L?E)tuMG=Hsp()aju&-N4(Rz=)0!{f(ZSOdhVYttbJB08Gn-k zQZ(S!8k$L~lDr*^=yv1TZfzLp>B`-RHo>B=hYMwSn)2d$DQ<$A(FRoI13w^N7QS>e zV^UTt@v8EY3lDo&IMm@h&h=|>dd#F(!0y%n$y}z~Q8jd*#VPxQO9;-y`dT;Q3Up33 z(zzkN8(-QqjM%pLHz7Owisro*$R}SJ;r0-^w;G>L24A77u`KHT+Sn#3>nH7)VVKDl z9r#W~(n{YaI8`G5D;*E;t zsVV{{rV3dMW(`bTrvCL zQT*nO;$mK}7$K$}6jJ%{s3RIX^ERRA%>>kfh71d$B&KP!i`niSIcYK5w*J$r)p~=a z^xRI0;DfycRH@GexoL9tpO6^G>h)ce9h#V{wRX#=p^V+orS(khDHVA zcq9!P1)R#&8%x?CR2EAceR5)%r4AU-3)E42VPfp-1ax1M4AApYvn@>p;FAm3W(i!3 z8@O&lTEsg7^&ENe_BL_@j3wg}eR;L24B3G5uWm38uq^@@oZ6SH%2u(bN(|&MEOtvH z@R*xcf$d^B(a*j}jU>~ptidg`p*)uni8n6^9RrjG!>$IvM&{=rk|G{q7TH&%?D6-_ z!KYJ%7kDGgc7RqQx2|uzeKmkK2l*-D)#)$NvT~ApE9Tw})(*|Lb$5g3^6s}MW{TbE zRtf-6M;ChQ(9SKbNX;yZf5`X#Hh0BUoJS}b!Cn?ZMDFk$$hq%NuO+67 zsR|aME_{x1@)#xpW*hQB9Z%mQzt$K30Qh;b!Bxn~DqhJ|sbH-aJoX>d7iv9%Ln)j z#^VYR`zx>&j>~>ydSPZ5N=d|SFXgDUi2oWF!Eub3l;q>b2yZZ(kciqafQS<2*i*Xs zzQWCu?na4d74s>rzQo$D(Mz;pHts|lL))C8JUE|u|5ri3snzI!mM~zFd-sZ2c`5PJ zXRi!S;*sR|a-jWHp#5@bihXWM8m4b)z7&mDzS)5a6c9?J)nl$|KlE>kPp#~D-BaqL zweYeZpUgvEi2tDcM}Gtfxb=v?!yjj`%tQc6vA!l)A|mps(a;a&OErH9q0rqvmxu=8 z1g4lDGO3Vq7gAF?9G&iQDh9?7HJ(M6fzga2UE+b|zHS4%j)yi{_LLx(N=IK+??hx?l8kbF|xeIF>$k3?qG=!nh36BcDE87vCL6M9E>r0i>1!iHP`$Cx{)^LB0TGh z58+LGy`*><=uL4id()Ji!j0Ed%em);$}Ge8bAK6n3)L`k5y0<-hJ z`AfKt)KF%78p$A0;rJKOtm7C^^&K|q!m=98lpU>Vc}s%36Z}AE(&9bU=m&X>V%pL$ ze0^piFeB&>j39zr(E`Y-(qX2RsC%Y4%>}DWsFWonW}S=d$BKiUz)=4TLAiCkH10=u zCO~(eZ#k{!G!EJ|VA=Tw4Z&fg!G#OzMPI$JvL~dM2TCg|WwR~Y>wOw28w6fS6M*LS zy-M5d0cU42OV`Mzi)?lH*73_G5ExPpEQs2d`eNsXaL; zc&J;|IZXu&+G&PtTShylV12u#PM6VrDtkVal1r5mChrfL+2u(OP`XlRdb*{UJ7gIl zNuHO@p~9Gx+F8R!`ooB&g~&U^+|{U$8eVbT_Vf#axEie1*t6Wi-^`uRAez;qV!foG zgCRM|s?Hs9@@;@9h4eR&`o}jllf%5lN7swUW=H6Xm2f8krs>7%y7`vKLC ztJy6*}8AC0YoXkuLnX)K_e6sP5(zVfEGTC$BlUD%32J&dLVovI1CG;yvQ zdGU$TVwS3(AkCD;rS}jKVlP%&iXn7D^O@n!k}P!L8}bToR*smhavmRK-}#}dEsr^BhKZ^%>Mp< z=}#bdc){)X46_1-Wu%0lMbnO4mP)c)Tl?3eZ&jK9qbi1L{O?u0UYqJLtFxsC{PUHm z3S}(aGG4ktKhWiMAhTESOC5{v{g|wFk8&}CuB@_A1^UHJ5clq4eKofAmXO8Tb2%j~ zAd&eyq4bNPQ{j-{?H(y=0N64NUnh?Mv+?mf!1MQ!M7yh3S9T6$MDr#rYVyoC3amHZ z8(kFUFvxb9bR33r$lgR+Ieag3woYX&4A=$pAwKlGd_wzsV+9w|Nm39M)xN@j4BXUD zOu;@9AflNTs99>oZId~Cr%RbOVTew#giZ{Vv2BzFowCvT_3}F~nO~~y7C#F@gk{_$ zIX;dr)viOf)<2gh0H7`scN<|`XHXKyIq=Aw>ytpZJo2ia3XtN*ym<4)1+L3 z5+7nWxevckl=+7avk~_0XSRUKk_NT1u9BNjOTayljercX-r1w)O?ny463l3W`K^Qy z9VHF4+R&k>A-0Boa1flYtkh9yd3=C%F<9(Q7#0iyFGpPNdl!v@;Bkba2u4&d;R2gx zO=Ojt7JcbyUG~{Q4ppW-rP7e4?9w&Wd}ipF+h_yuock=L`Ww97`)1)*N*ssHV1Qmb zk7`_151oO4htpRoU~IXXYr!5%0jCpS2h>0maIW!hgz659P-zE_(JWf~GX&gQvubjO zr{wN1!#qcmhd@781N)6tmrS)bs*QuTN|JqDtz!y>G(?bL{BI6nw@8VMk^d>)KMGFL zXq^4VWi@j)z=482d24b-MZrv;3vX$Gm+*#0(CTLwY|WbQFhznV=>Ub`IoN>v;RcKA zDK)*;x<1gWgiAfzT(p24$(Xsf1Ll1k)a>C;<3^@2pd>9{!%BmvRA*13OHa~};OuiW z8Fq!SkmOeN>T<;|KRYl-1+~Ej8u2Vm;Thn!bMrEO)9& zLEU`S9|9yA29&&Cx4Br3q&BDP*ia;T-Uf)MA{4Ug<;^^+lFe!o(_i0`g7w|A$r-3- z&1_v5Z3JgFB&Iir*l(8!w(|s3)MZ@;o8`ffoS_w6f4Swa8d+5d5t*ARI{j(kRC?RO zcnjOy|6_k44j{tT8DB%z%#bC!FdCV0TSIP3xEJe36{{PLlK;kDvPI&Z~I=?hGjio3AR*mkMytYNbeG|L4_e-CmRwqJ(|c<7QhNhXJ~h z2b3iEnl9=SVvSsbq~A9vvFFK7kQfs<0(JzjZ}1NkUVMexGAN>!@IFA?ko8l}t0E^c5A44xKHUKr zfVR7zFd2Yq*=cF;NI+t3W3tE1KH zWeP02T>xVidnyU8>iBlDUO7}2=&@l(?)Q7b3z-??|A*FOt#PS$UK zTypQSN3!hpndf72HZvS$R?T*?k$B+ZSmM7RrFk#Elhl zJ@93FdJ$CSPKpd}R z!GF`h5nG1pW0(&;T*G)O;h-n)_z8!gxSu|hy!pBE-}cUyDDn`}*gkxpUVYBUiBJ#4 zYU}=kz#)3OCz9cy1j2|k>0z_j!@?-iyjtLJ;p>-19N(F=yWWUUJmG$}Ckpy~-sZiD zswVs;na64)6~$OlF3 zlh2(vzBV1IPCEe^)otoow)ig9$3Jl5^zZL7D7YsIDIAy(tZSdd)o?N`=Yy#f18k-+ z1!Pp<@d9O9b3Snume4XIse?T4nf%%o0NV)aJKKAStSgcVHJ|9%&ClH4ptLl(H+;BL z`A=6e5(hXYHRv7XhKTp2VLJ zGPmATng2ewg4-9|ybw(NUc1082&5)R6sdm`v0_^GRHn8dF0&sFcYW)WYU z35{A6e)6WOYCY>F+VzT0dk!A%@A$+-X$blPnJo1lx1<#>Fih%>cL7jFw#ZfyQP z?R|ArRPWa>A}I|5BBdxG4N^lZjWiMi44u+2gfystG>X8`BHi6Jf`oLZba!{%8SpE9 zf9qZAu66%-@4aiiYt1@~Ih=F$ex7I7+55AfSyT(&#u<2+z3_uUUyjGDI$>ymjQ$$F zaIFM@F|;6mpUUMFE>ztJ%h~f;X^W`v2@9-Q*KA8PvR70Gst@pNQx!skpN+lZ62a4B z#v9735okqWKVwh9xYS2eaXuMEk)dMafoP1j$)cZn5a20$NbE2~lJocRm|5v*a22mh z&T`*TPA2NYn3p8T&lrXQHs=d|YMDm8wLqwA0of#I-ZdX{6+o8GUw^d=1Ol+7G!yoL z9gduGm+}ZI(yMB4)K&G6L>(r(f{paT@kY$Pddj2I={qx^iqquq#d` zpJ{8_`DLn3l73ha=ZUGzy+c@3fLo+jA*OysyC+?ieWZ<@J;bLU^iK)P3n@exagG6k zMGzuH`Mga-1Ung5ouYP1CX2{~z}++@pTybHfxU;s$>9+kI_z0(SJ<+A6eA-s|1TNWI@2?|bFuwQt>gAM56f^zlv?hdkD}@}{vX5s~mx7PsaNX{+xqeJFi(Xr$`0tVX;+*tX2Jkq~?fgMi}E z{tL*Nw{8!5P*&v#Bli8MWLDV^OX-P48fdF$HexIOd_G>oDKPT*+D*7}$!Y#CE z{D7^WSu55n&-Tp3Zn?RJC`iOXA1n{;A{DQjisuhL@i^l__`6y>gBIj%#U#&)!_E31 zU3K{${@5UEXbpIgvCFB|T|Skcispx;C0dMQg9HiMuWn?A$!gb{M1 zOEscsSb5@VkGx;`5r`B%iit_^38x9g{{az$PnCfL46U#ZETV>U@=R)a`Y?1ljZ#I= z`5Yh& zkL88bM#VaE=}%Tzqu)tUW8G=2Jl0hE%^GF$t+Sxj#KZ7lUU1{#0r5v*h%vt3c0|5l zaC8LW+m$5F;}wIfrW2DXA&g?rg1fT2!=M!j;&w$wGsn5o3v~Xgea5Hes&iGv!%L3P zYmnU|Z#+1N1drxAzwT{^-T|FEI!-Avqpmx?+da2S*lkFH!R6%hFQ9(if0B$qTQ+(x`PlKd=&ML}n{a-RhnKAa2 z;>sJ}Q)YI@kbbCAnDcbQb!R>ZsETpE;6pN-Pv+Z__=>heaAvRssLE{2*e5qwOr`$o ziKykr`kQH$fuqgz^{C)Mbr6kQ5Au7SIKN>z5Mj$xV%V?2@28R(5!rUw)6$4xT<$qIlIYMu4QqF zcrKxuAte{7ZfB!j&oPxcT~_->kC$P##DORXuKG|2L4p4xmG_i7XG8kohw;Y~8-cc4 zfgZo?J15fk)PKxn^=*uM@OYG}6SjL^{fr88SycWNQ>jXX*(G>^2Rc0;UD4vC@UWq$ z=-M|vIGACe9RHX!p*Fqgmp*gFwWd$@ye>)Mu0m$D8wB&S)U~Q+!T~6&T17n8lIY5=<70#p8jOZF8h3}gn;7_D$Wf6O*nB%K@yB!Z^{6a3^vtALKta7X++9LT4jr*& zbS*{k!;e^Q*7^eiGCSp6xY7eKk#YnBE96ACQ%S9w-o+%@hsp+r&bAyRLQ92|E`qks zq)EF7zvrx5C83h89~xV@&fl*IbKLkiE3G!gB-qKm0K_l0LFgx*^vyaA+}S5!FWh+} z-R#HfL)WPnS6GBWn^;29S;yT;&mMg3lb|r##pw5Gt|uQzAQ|A`)I~C0q_1vX@6y!| zm~z6V2s7qiZ7c_JfVbB z^YyhgNKO}%5#X3oZ#T}&vUlo`w6xTi38S@}%ra@KIV(|<>9dwV`!(mbUMepHRZU!zpJ)8;XPwZB9 zTm(W0OdF~w;{*5>zT#5@-SOO}8?=D74LS-N{CCPdj5sNzstrx~EC4%eno;^^FUS&s z7s_d}9yh`VfteAm_0$7q)=-uQ3=ep@w*o&GAb#j$(o^h*5MGkviWeQawKMS zYIK$K!>}_#_N0&(H^s-1m%?36IcBKEc(4nK7YP!lbWP;nSzg{Axb!@de?(UYo8LgmD3~3qiDI|*J z6>=I5=x25#X@5?8LFbSx@OtM}+NU;T_igDKv&GKib*De`*qE>LjEdQ`(0FG}-3bMkZ= zKgaVzhDUGPKIf{@EJyx~!;dxj(z4i0=E?QwT5-SPuu~#mFQN&ebl2#|2ps07;y!u1 zf`?PbLu2nKa_i(kMZ5VtD_~WjAZZ;{94bW+ZQY)=iP5gZ_%^N386d{@M0(O?mmP3c z*Z(7Q)K}+XSM;2IB7pweFzA%`A_|*MO42}ymxThPr&j~4Y!oA{FqOjYT zA6^}Yl5XQ|hOzMrMZy*evXA6jC@te6h;*X|BG3~6^X2kfg#w7) z?_1bkmghKgPNvO)&b=$kt9wx6@>Z(4mmHh|7$)9O=H4LpyOXwsyRW}t7T-YI{^e6} z?qu(A4sh3Ttu{~8nXqGwo1=^*98%C?es~Hy`PiC)_Bv+#%_|I^hON#@J1bzoN%Sus zV3t%`m({U#V-%+pd21bPdoXD7-Adjl*6ui_XC5m#IhoSmVs-m>H*SNc#3?(}69)!B z{m)eKbys(YB{SPohNVW6I0X`0X-&=8JbQ^T=uI0P-+Y(E7j!BKff#%r(D@=^j)QLa>w5S`N-Q}_^V7*kpFY{uJ?bfn-|i5 zAAhwgFQTQ@RnKMvoqJH1bT=On&Dnz#dVT6ou7Wg$ zt>=w*|5n853|MO}Gf6-N6Ul5u#wzcfEu3@qNp=}UNzF1DgP{~6hS5{Kwm4rLO^=UG z&-5!LLo;mzBK@N7|1^63x0dzF=mMj8Ro^=Su&0ApJ>GQrR(#a)G<>LmqFkHsic?SJ zocM-0tA zFyQGVtHfB6u+ev-ANS0;#B-zDRZx9k?QhkfGas)j+o)E^OZ_e89Z!3}@4hd%DFLXW zk#T*GYvWa3Hsxw@oWmbTAx5T|9fZ&dV#R)Qxuic@vQcgIP7ymIlS*a$ST#K7nv}l( z?u2bP;#g)|R0iFQ1pO()k->Wt?zH9*iD0y0uacccB}-EkX}1J}=tQla<*X53N;>_~ zr~E{hM7vSXm@_Icvz0J3Md@g zW2__WTI;SOO7u_k%`(Ji>wbM)e(v+h54vD+WMX6HiIGL+``91H#c2b@jeGy=<4=mW z<`Du!!j9D~gFw`xc#Vyb(>nz}PM{R#=vtw5ds(w)yA;oBMMhfi3-xL2pr1E>A1wm( zJP-}TmTh`<(ktxl&sFj~RY<Vi?>+lp=o z7`(C_bF=TYhbvLIRfp$L{ik%D8X%^ScisQ$kfkwJW z9+RZMO^Nf-OfhKQ0$;(+&BN3cIepA&I6IsOvp1;(fQgcks1+@;!o^DOpVXp|k2(fn#oi$wQs* zp1y|}vH_ZYyK+g4%mZht#eMZ+PUb68*+F8}FHS&{CMS=GFqZ|#dRHe#R@eRb1c||} z^nDY}r;*cRr-R3ChP8UELAOx=^v5Hc5hO4_KWTcayobV%qS`}bHz7wg4J@@#2hjaBRwA_g9%Hw0ZV;>NVN@RZ zKzt@sGJbn(LHQ-7C==;+dsv0mvrbc4|D&JC2UCseS^@`_$GQd;!;U|O<&(fxAkr$XHS z-b$v(w-!SHiAc=2sz+BV$mfDxkyPpt-W%xdvcvNJB(MM= zXRTK;g1F;61fmiy${lA%CUgXx%|F01GN`h}E~|tLsU#8(m{}{z8CE0)K+uh56smtb3de7nl#Fwv7)*war*YWXQ5c{IJtRFV&&U+i4V3S3C0Mg0qsFp0Yqt4& zPu-rMJnjf+5`8QCJ$f_sEn|r~%kwM9E0N^u4qTe?kTudX@Jp0gr*j z&(nohEWc@yTv?QEiFUO&RfDd42enRA!RCt@#9Cism?Ft0Ky3?l56wiRMAYN}r6(jx z!HQ?<*|x$WCq(C`GzR8RLW>i482;<9#DznE1y&g*6aUUgs`hM53M|Fi5*{1#ycA;9 zu*XTPrcn7-9+a7QbR3p~n?c9axvf%RU^ zTsm5eY=5K6NZp)4E`D9(P-@zKE1MGcdCF$Qgf$F|B6h!prji-1{ZuA!1z8(;#A5Do zNc|EyFub-c5YXL_x*6f@U-Ji!25UO>_Ogm&a^m3moc&8&T!{q_fIY0H6MOhm_T&74 z=&HiR#{Intng=3p*;C0L#p6M``nRn#Wz$w3G?;dr%4p8xe!*}zH)_FhI4&G;%)zMl zRx^~JyBk^D`_z?>b~YEla_-!baY5>szBrxozJI#=kn=NM=vSG2q8jBh_ncM531+gQ z@E@Ma-OaBLgGU%@JByfG8s49}IBYno7gLS-tJfyBw3sJ0^YX-{47xLSt!sTat%x@F zd7dD*&zlQL=*nvd=n;Cnpyj8=hLimyo7KGam&rE}GpWdx-vYLSVp_xAJ1B8sg&uoC zNNqrv<-;_MrP&Ho(wGIoD#mKVsBn{=4zz+owAesk80Hh}UXdmV+(vc4i70ELIw<55a2B<~<0OC4L4vEdn zM+cV{ih!?rQ1_QcWHYiZX&F~uixsEJH`CB!tiD|14p5*urCrMg1|h#W@)xbSv&YBM zA|IzrmkpEnfQLX5eFHGhdDA>OPSH9S@PqS4Rel+%u{=JQv>dOvuD9jhNTZz%w?@ zUzv!tlyHySDNGA2_;+k#R{2Na)LTTb26XzInI^+FxfGE4~QVnjL-QMNW5y@ zWce_pmFZYzj%$Ae!>HO0t!=ZJyO&zCkM89}w7T_M^KH$y-|Du-gn3ZhoaV6ijaO6W z0WjcfR3Pb7I%1`Fs8S_dUhYz^B_zuqy;*8MWBd|5jGs@Bkjc!3J*%cpbQjadsd{I9yXl^^=+F#~PbRfevzPRKeubvT@7{8FDnR!+9>nII zbLN@Bu|hg3+m*+9&Pa&m&xwz~rJ4Dp1yign{n42bZ-?)>;VXjgK{SIKi-vz{<*@B2 zJ`i-!Wmt`dcpG=9coY?sG&w!C$gjkS4d5X&X_wDItCYz(`#JjO)tgKZkwA|1NSC-u z0IWb>LKwIFjj?Wgxenbk&QM?7Ij(hK`yyFi{!cE1AP`QWO}}c3K32Z<-S;O5HKcb+N$m;$w@}Q~>GX9};0N=n!G<`@5hhKG(5@qUh}6_>%$8`aZbYdk7M}z{tQ@(C-fo7zfu}9G_c#lSFFH!f8U=B z>r&@8GyI)oyJ4R-8eSMptM0744W?lkq8J#Zj8Fe+-u)j&|F6#I3s{8|-cK(Jpx(Y3 zubz6q(}ox_S6@3(J5gObF}`-iVCh{}l3IpV@=P~Sl?sXNJFfqSA0Kta?h1>}Vz{qi z5G?qNeIUld>(Lbx$@uKWAt@~Oc!iQG9b)&4XhUm!?cj2AVsmoyw4-aIe$9SmL*UEW zy3^-FIp zhJTQ&*v-FT*O=1W!Kf@PgvQyOMymgF8|*w}36aRo+`!v8q1wh3?U5G(;>}?M{}Jo> z=M;)RB<|?k``|W8|Ii0^MF+R{=EpoOQdPC|fk};LlUimR>D))MSzBbA2df*lTEC)) zSL|PCeX5z{fhcuX#=XUM0HeVl{KEYXBG{es6&{S`N_2Z#`)=~a)q7aZL&dh>mzhV^ z`y8P^y0)@F)m4&A@6^zz9C&&5h`@t}Ki z=KA#nFILX%BIZu6I{1~EyP4!vwNO42r$nJQd?Hv$@8nPQ6!Rn+z!<)Rw2BVN ziQ`^iiq4Qba$4DF#5fpZz}P?4l;#NYkLZ?h3Rzp@i#yq1Xb~5P|U3hYsk_S>)^=CHCn)Y1*JerFp7|S?Y<+AhHO7 zL37285j~gs8@XM*r|iKuED$7oEt=Vg6N(cqU?6FhuT~sDqf)r8iR5IRIWt^t%D0%j zmwen;WtBaFS@kh)&#Y|t!v|7k&P!58)y)U3+y<-C5~T%ez?}k{JTU=*O%iiKl407s zRx{gk`@+14%!~w=Ckk01H_O6~gx10SVnh@+20=z|avk>mT5438eJpxhNJgbho{*lf zd8%4vRRQ&~?&Bf8xd7CI|+4p+8E3Pg`sqv(Jy;iN&{Hs+K2 zD8wDp{$n;{k?1Ra@~(bHOCdDkana8|j}`TiW0G@iAq!SYRLvyyVZ2K{qv&Vx981&O ze6{@f17Ec`D5Q#rBt;{g*LQ#KdLr-HQrVzb?n$5PH_4jF687X+!icJ z=@tm(C_8~(^Yk~8$;Kv3qW7PX_K2>1@z}OpX-f2S=$X^reXTfQucNx7ul}+{+nU`V z=5AWzn7P!NPkR5DqqOjvS||gBRJ1qQCjK*|2$H}Rp)1TcTOR3t)z3Y~vS|(FR!qeI&u6IN6w)!+PL z`)LM3QZ-Z8%DpFPVzE0U%fqz6Z`4eq=Id5sNVQ5T`K-zmlj(-E5Ky$ z{PhlRqBuP2g1>bXzKjECJ&mBo?dOvtQB=EAMw(pz10<-CCEpx5?(;TMpB8^_b zl%K~Hb#5xL9$?5c=nQEDuFUj^NQ(i;=GPNSjAnSZ38?un6!g&gmn%mO1DBc=?eobr zi6a$lqMH38qEdNciB1f+E7mplZtQ5bp050ZHXF>Pus#lZHg@-4$=$pxchd&P`;+oL z(o-L#RI8P~^6S3rOL~op0UJ$E2cn6i9!D{X<+XxixLLS@%B{G)_Zi!X%VfXQm$HQ6 zK%T8JUvbThke9AoEd_$vqUils$J*s0xW16sXe08UDG8*54-`|wv};2huKgK1^0J;_ z{|5os0K7Q81GyGEFkBnQY>cwSfag>T?BMK6C= zaxzBgqfL~#b!vmEqevf@xeKD)smT6ag*U!|H6dgf`}|Q1*&07(D&3#VZM+e9tCoIw z?kGx{m#8qj&l+5pOakT*d7jk@gnLdin9g&OS-=&c3^}dkS!{m393$yKv(e;%Lvpxx%X^FV(3P?0Pg7(xH$nSG3UP zj$@-O+ksLweGIry*1qFw$gx$Nz5tU63M7;>O-?qIhtY0}$nm3$G-rGVfm(Wd$W-ja zOM#NJr#duT#ZU`k4v&=ALgSi3jKuQmq=Vs}z?3-xf4eJ(l`5ezS>Pemh!}1%p1i;T zW1*75r%&sS)22~BcW?Yasd;07lVtB#QYH3A=Zj7^Z^^{C$9N{1&7~$$_6LX58l{h{ z>q@(2P)+JUXYudp4)zE6ti|1HlTTIHK9#EwcFdk<6<^OxjI0;*mxX-XA(a`` z(AuJWa(mK6GVj3h&p1E05n-i;4X(pKH2m6iUPm0~%rA5g&j`}oVQj%KfgQlGFr)i_ ze~~F8YbZ8dJh<)&efqxgo85Ds2~^rA|2zckk*>~d__HiiK&kxne(4me8Qfa`j&-2E zUOvCZfVy%#?(K}X^wB!NH*pbvw^oI~KNrJpvp~m%SV}43+^`9sM;2tXjyks-j(fMy zj70DrjeL6WXEbQXN?y?Q_Lf)4w-MI=Jm~+qIeL+i6d>;Rr3;x5$M{P^%zlpk^e2LZ zv(ankcOz6#X99rVuhW-DeL$SOzLc@|Jg=P;J}KX*Nmm?rjykv>j6 zDY^R>;ZvCFJr(>y^~XAKRKi|%ehMLQQ2x22rzJ@YX7G*<{cZvO})g7^O^OWU5R Y1;)nru`=O534|aeCMTLFto!bN0g_1jzyJUM diff --git a/gauge.go b/gauge.go index d5abbcd..cb03eb0 100644 --- a/gauge.go +++ b/gauge.go @@ -20,12 +20,24 @@ import ( g.BarColor = termui.ColorRed g.PercentColor = termui.ColorBlue */ + +// Align is the position of the gauge's label. +type Align int + +// All supported positions. +const ( + AlignLeft Align = iota + AlignCenter + AlignRight +) + type Gauge struct { Block Percent int BarColor Attribute PercentColor Attribute Label string + Align Align } // NewGauge return a new gauge with current theme. @@ -35,6 +47,7 @@ func NewGauge() *Gauge { PercentColor: theme.GaugePercent, BarColor: theme.GaugeBar, Label: "{{percent}}%", + Align: AlignCenter, } g.Width = 12 @@ -64,16 +77,27 @@ func (g *Gauge) Buffer() []Point { // plot percentage s := strings.Replace(g.Label, "{{percent}}", strconv.Itoa(g.Percent), -1) - prx := g.innerX + (g.innerWidth-strWidth(s))/2 pry := g.innerY + g.innerHeight/2 rs := str2runes(s) + var pos int + switch g.Align { + case AlignLeft: + pos = 0 + + case AlignCenter: + pos = (g.innerWidth - strWidth(s)) / 2 + + case AlignRight: + pos = g.innerWidth - strWidth(s) + } + for i, v := range rs { p := Point{} - p.X = prx + i + p.X = 1 + pos + i p.Y = pry p.Ch = v p.Fg = g.PercentColor - if w > (g.innerWidth-strWidth(s))/2+i { + if w > pos+i { p.Bg = g.BarColor if p.Bg == ColorDefault { p.Bg |= AttrReverse From 83a3a236f45fda80bc4339bb568dcafa984be022 Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Tue, 21 Apr 2015 18:07:48 +0200 Subject: [PATCH 06/13] Renamed `Gauge.Align` to `Gauge.LabelAlign`. --- example/gauge.go | 2 +- gauge.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/example/gauge.go b/example/gauge.go index a05a2d9..b703358 100644 --- a/example/gauge.go +++ b/example/gauge.go @@ -54,7 +54,7 @@ func main() { g3.Y = 11 g3.Border.Label = "Gauge with custom label" g3.Label = "{{percent}}% (100MBs free)" - g3.Align = termui.AlignRight + g3.LabelAlign = termui.AlignRight termui.Render(g0, g1, g2, g3) diff --git a/gauge.go b/gauge.go index cb03eb0..0d2677e 100644 --- a/gauge.go +++ b/gauge.go @@ -37,7 +37,7 @@ type Gauge struct { BarColor Attribute PercentColor Attribute Label string - Align Align + LabelAlign Align } // NewGauge return a new gauge with current theme. @@ -47,7 +47,7 @@ func NewGauge() *Gauge { PercentColor: theme.GaugePercent, BarColor: theme.GaugeBar, Label: "{{percent}}%", - Align: AlignCenter, + LabelAlign: AlignCenter, } g.Width = 12 @@ -80,7 +80,7 @@ func (g *Gauge) Buffer() []Point { pry := g.innerY + g.innerHeight/2 rs := str2runes(s) var pos int - switch g.Align { + switch g.LabelAlign { case AlignLeft: pos = 0 From f99b800720c209fab6dfd96a3a752c0c2992f1fe Mon Sep 17 00:00:00 2001 From: Matteo Kloiber Date: Thu, 23 Apr 2015 12:19:21 +0200 Subject: [PATCH 07/13] Fixed a bug which would render the background color of the label in the gauge widget incorrectly. This bug was introduced in changeset 137d2a7. --- gauge.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gauge.go b/gauge.go index 0d2677e..986f4f3 100644 --- a/gauge.go +++ b/gauge.go @@ -97,7 +97,7 @@ func (g *Gauge) Buffer() []Point { p.Y = pry p.Ch = v p.Fg = g.PercentColor - if w > pos+i { + if w+g.innerX > pos+i { p.Bg = g.BarColor if p.Bg == ColorDefault { p.Bg |= AttrReverse From ce454da7923ec6c9a0a6d29f14f560ce260117f5 Mon Sep 17 00:00:00 2001 From: Ivan Daniluk Date: Sun, 26 Apr 2015 17:35:57 +0300 Subject: [PATCH 08/13] Make sparkline to use last N datapoints --- sparkline.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparkline.go b/sparkline.go index e0725d5..c63a585 100644 --- a/sparkline.go +++ b/sparkline.go @@ -106,7 +106,7 @@ func (sl *Sparklines) Buffer() []Point { data := l.Data if len(data) > sl.innerWidth { - data = data[:sl.innerWidth] + data = data[len(data)-sl.innerWidth:] } if l.Title != "" { From 9f261ed9a2880357584c846f155f361cd667df91 Mon Sep 17 00:00:00 2001 From: Zack Guo Date: Sun, 26 Apr 2015 10:45:51 -0400 Subject: [PATCH 09/13] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cdaf27..4d0a34c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # termui [![Build Status](https://travis-ci.org/gizak/termui.svg?branch=master)](https://travis-ci.org/gizak/termui) [![Doc Status](https://godoc.org/github.com/gizak/termui?status.png)](https://godoc.org/github.com/gizak/termui) ---- +## Notice +termui comes with ABSOLUTELY NO WARRANTY, and there is a breaking change coming up (see refactoring branch) which will change the `Bufferer` interface and many others. These changes reduce calculation overhead and introduce a new drawing buffer with better capacibilities. We will step into the next stage (call it beta) after merging these changes. +## Introduction Go terminal dashboard. Inspired by [blessed-contrib](https://github.com/yaronn/blessed-contrib), but purely in Go. Cross-platform, easy to compile, and fully-customizable. From 32027f061cb5902a72b6f7350f7da65f621e13da Mon Sep 17 00:00:00 2001 From: dhilipkumars Date: Mon, 27 Apr 2015 20:27:11 -0400 Subject: [PATCH 10/13] Add Code to display Y-Axis Scale Center the bar's labes Add a screen shot for multi-graph Update the readme. --- README.md | 5 +++++ example/mbarchart.go | 10 ++++++---- example/mbarchart.png | Bin 0 -> 20075 bytes mbar.go | 45 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 example/mbarchart.png diff --git a/README.md b/README.md index 4d0a34c..01458a4 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,11 @@ The `helloworld` color scheme drops in some colors! barchart +#### Mult-Bar / Stacked-Bar Chart +[demo code](https://github.com/gizak/termui/blob/master/example/mbarchart.go) + +barchart + #### Sparklines [demo code](https://github.com/gizak/termui/blob/master/example/sparklines.go) diff --git a/example/mbarchart.go b/example/mbarchart.go index 7729104..a32a28e 100644 --- a/example/mbarchart.go +++ b/example/mbarchart.go @@ -18,10 +18,10 @@ func main() { termui.UseTheme("helloworld") bc := termui.NewMBarChart() - math := []int{45, 75, 34, 62} - english := []int{50, 45, 25, 57} - science := []int{75, 60, 15, 50} - compsci := []int{90, 95, 100, 100} + math := []int{90, 85, 90, 80} + english := []int{70, 85, 75, 60} + science := []int{75, 60, 80, 85} + compsci := []int{100, 100, 100, 100} bc.Data[0] = math bc.Data[1] = english bc.Data[2] = science @@ -33,6 +33,8 @@ func main() { bc.Y = 10 bc.BarWidth = 10 bc.DataLabels = studentsName + bc.ShowScale = true //Show y_axis scale value (min and max) + bc.SetMax(400) bc.TextColor = termui.ColorGreen //this is color for label (x-axis) bc.BarColor[3] = termui.ColorGreen //BarColor for computerscience diff --git a/example/mbarchart.png b/example/mbarchart.png new file mode 100644 index 0000000000000000000000000000000000000000..9a4252616fb8f753ca79a34bc0bf87d0df17502d GIT binary patch literal 20075 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 len(bc.Data[i]) { @@ -122,7 +124,16 @@ func (bc *MBarChart) layout() { bc.max = dsum } } - bc.scale = float64(bc.max) / float64(bc.innerHeight-1) + + //Finally Calculate max sale + if bc.ShowScale { + s := fmt.Sprintf("%d", bc.max) + bc.maxScale = trimStr2Runes(s, len(s)) + bc.scale = float64(bc.max) / float64(bc.innerHeight-2) + } else { + bc.scale = float64(bc.max) / float64(bc.innerHeight-1) + } + } func (bc *MBarChart) SetMax(max int) { @@ -140,9 +151,9 @@ func (bc *MBarChart) Buffer() []Point { for i := 0; i < bc.numBar && i < bc.minDataLen && i < len(bc.DataLabels); i++ { ph := 0 //Previous Height to stack up + oftX = i * (bc.BarWidth + bc.BarGap) for i1 := 0; i1 < bc.numStack; i1++ { h := int(float64(bc.Data[i1][i]) / bc.scale) - oftX = i * (bc.BarWidth + bc.BarGap) // plot bars for j := 0; j < bc.BarWidth; j++ { for k := 0; k < h; k++ { @@ -167,7 +178,7 @@ func (bc *MBarChart) Buffer() []Point { p.Bg = bc.BgColor p.Fg = bc.TextColor p.Y = bc.innerY + bc.innerHeight - 1 - p.X = bc.innerX + oftX + k + p.X = bc.innerX + oftX + ((bc.BarWidth - len(bc.labels[i])) / 2) + k ps = append(ps, p) k += w } @@ -194,5 +205,29 @@ func (bc *MBarChart) Buffer() []Point { } } + if bc.ShowScale { + //Currently bar graph only supprts data range from 0 to MAX + //Plot 0 + p := Point{} + p.Ch = '0' + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + bc.innerHeight - 2 + p.X = bc.X + ps = append(ps, p) + + //Plot the maximum sacle value + for i := 0; i < len(bc.maxScale); i++ { + p := Point{} + p.Ch = bc.maxScale[i] + p.Bg = bc.BgColor + p.Fg = bc.TextColor + p.Y = bc.innerY + p.X = bc.X + i + ps = append(ps, p) + } + + } + return bc.Block.chopOverflow(ps) } From f23ed68e303d86b6c897e2c43c99961624d24ef6 Mon Sep 17 00:00:00 2001 From: gizak Date: Tue, 5 May 2015 10:55:35 -0400 Subject: [PATCH 11/13] Fix https://github.com/gizak/termui/issues/43 Add termbox.Sync() call before getting terminal's width/height Adjust examples see https://github.com/gizak/termui/pull/39 --- example/dashboard.go | 6 +++--- example/grid.go | 16 ++++++++++------ render.go | 2 ++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/example/dashboard.go b/example/dashboard.go index d855ef2..c14bb44 100644 --- a/example/dashboard.go +++ b/example/dashboard.go @@ -47,7 +47,7 @@ func main() { spark := ui.Sparkline{} spark.Height = 1 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} + 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, 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, 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, 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 spark.LineColor = ui.ColorCyan spark.TitleColor = ui.ColorWhite @@ -119,8 +119,8 @@ func main() { 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:] + sp.Lines[0].Data = spdata[:30+t%50] + sp.Lines[1].Data = spdata[:35+t%50] lc.Data = sinps[t/2:] lc1.Data = sinps[2*t:] bc.Data = bcdata[t/2%10:] diff --git a/example/grid.go b/example/grid.go index 9dbbc11..4912141 100644 --- a/example/grid.go +++ b/example/grid.go @@ -8,7 +8,6 @@ package main import ui "github.com/gizak/termui" import "math" - import "time" func main() { @@ -39,7 +38,7 @@ func main() { spark := ui.Sparkline{} spark.Height = 8 spdata := sinpsint - spark.Data = spdata + spark.Data = spdata[:100] spark.LineColor = ui.ColorCyan spark.TitleColor = ui.ColorWhite @@ -93,24 +92,28 @@ func main() { ui.Body.Align() done := make(chan bool) + redraw := make(chan bool) - draw := func() { + update := func() { for i := 0; i < 103; i++ { for _, g := range gs { g.Percent = (g.Percent + 3) % 100 } - sp.Lines[0].Data = spdata[i:] + sp.Lines[0].Data = spdata[:100+i] lc.Data = sinps[2*i:] time.Sleep(time.Second / 2) + redraw <- true } done <- true } evt := ui.EventCh() - go draw() + ui.Render(ui.Body) + go update() + for { select { case e := <-evt: @@ -120,10 +123,11 @@ func main() { if e.Type == ui.EventResize { ui.Body.Width = ui.TermWidth() ui.Body.Align() + go func() { redraw <- true }() } case <-done: return - default: + case <-redraw: ui.Render(ui.Body) } } diff --git a/render.go b/render.go index 735fe5b..d697d0a 100644 --- a/render.go +++ b/render.go @@ -34,12 +34,14 @@ func Close() { // TermWidth returns the current terminal's width. func TermWidth() int { + tm.Sync() w, _ := tm.Size() return w } // TermHeight returns the current terminal's height. func TermHeight() int { + tm.Sync() _, h := tm.Size() return h } From 672baf23ee2c0f5badeedd00c0e3e3f5a5e7b3ab Mon Sep 17 00:00:00 2001 From: gizak Date: Tue, 5 May 2015 11:08:45 -0400 Subject: [PATCH 12/13] Fix https://github.com/gizak/termui/issues/42 Merge https://github.com/gizak/termui/pull/30 --- grid.go | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/grid.go b/grid.go index 6d86294..5f6e85e 100644 --- a/grid.go +++ b/grid.go @@ -13,9 +13,9 @@ type GridBufferer interface { SetY(int) } -// row builds a layout tree -type row struct { - Cols []*row //children +// Row builds a layout tree +type Row struct { + Cols []*Row //children Widget GridBufferer // root X int Y int @@ -26,7 +26,7 @@ type row struct { } // calculate and set the underlying layout tree's x, y, height and width. -func (r *row) calcLayout() { +func (r *Row) calcLayout() { r.assignWidth(r.Width) r.Height = r.solveHeight() r.assignX(r.X) @@ -34,16 +34,16 @@ func (r *row) calcLayout() { } // tell if the node is leaf in the tree. -func (r *row) isLeaf() bool { +func (r *Row) isLeaf() bool { return r.Cols == nil || len(r.Cols) == 0 } -func (r *row) isRenderableLeaf() bool { +func (r *Row) isRenderableLeaf() bool { return r.isLeaf() && r.Widget != nil } // assign widgets' (and their parent rows') width recursively. -func (r *row) assignWidth(w int) { +func (r *Row) assignWidth(w int) { r.SetWidth(w) accW := 0 // acc span and offset @@ -71,7 +71,7 @@ func (r *row) assignWidth(w int) { // bottom up calc and set rows' (and their widgets') height, // return r's total height. -func (r *row) solveHeight() int { +func (r *Row) solveHeight() int { if r.isRenderableLeaf() { r.Height = r.Widget.GetHeight() return r.Widget.GetHeight() @@ -96,7 +96,7 @@ func (r *row) solveHeight() int { } // recursively assign x position for r tree. -func (r *row) assignX(x int) { +func (r *Row) assignX(x int) { r.SetX(x) if !r.isLeaf() { @@ -112,7 +112,7 @@ func (r *row) assignX(x int) { } // recursively assign y position to r. -func (r *row) assignY(y int) { +func (r *Row) assignY(y int) { r.SetY(y) if r.isLeaf() { @@ -130,12 +130,12 @@ func (r *row) assignY(y int) { } // GetHeight implements GridBufferer interface. -func (r row) GetHeight() int { +func (r Row) GetHeight() int { return r.Height } // SetX implements GridBufferer interface. -func (r *row) SetX(x int) { +func (r *Row) SetX(x int) { r.X = x if r.Widget != nil { r.Widget.SetX(x) @@ -143,7 +143,7 @@ func (r *row) SetX(x int) { } // SetY implements GridBufferer interface. -func (r *row) SetY(y int) { +func (r *Row) SetY(y int) { r.Y = y if r.Widget != nil { r.Widget.SetY(y) @@ -151,7 +151,7 @@ func (r *row) SetY(y int) { } // SetWidth implements GridBufferer interface. -func (r *row) SetWidth(w int) { +func (r *Row) SetWidth(w int) { r.Width = w if r.Widget != nil { r.Widget.SetWidth(w) @@ -160,7 +160,7 @@ func (r *row) SetWidth(w int) { // Buffer implements Bufferer interface, // recursively merge all widgets buffer -func (r *row) Buffer() []Point { +func (r *Row) Buffer() []Point { merged := []Point{} if r.isRenderableLeaf() { @@ -204,7 +204,7 @@ func (r *row) Buffer() []Point { ui.Render(ui.Body) */ type Grid struct { - Rows []*row + Rows []*Row Width int X int Y int @@ -212,29 +212,29 @@ type Grid struct { } // NewGrid returns *Grid with given rows. -func NewGrid(rows ...*row) *Grid { +func NewGrid(rows ...*Row) *Grid { return &Grid{Rows: rows} } // AddRows appends given rows to Grid. -func (g *Grid) AddRows(rs ...*row) { +func (g *Grid) AddRows(rs ...*Row) { g.Rows = append(g.Rows, rs...) } // NewRow creates a new row out of given columns. -func NewRow(cols ...*row) *row { - rs := &row{Span: 12, Cols: cols} +func NewRow(cols ...*Row) *Row { + rs := &Row{Span: 12, Cols: cols} return rs } // NewCol accepts: widgets are LayoutBufferer or widgets is A NewRow. // Note that if multiple widgets are provided, they will stack up in the col. -func NewCol(span, offset int, widgets ...GridBufferer) *row { - r := &row{Span: span, Offset: offset} +func NewCol(span, offset int, widgets ...GridBufferer) *Row { + r := &Row{Span: span, Offset: offset} if widgets != nil && len(widgets) == 1 { wgt := widgets[0] - nw, isRow := wgt.(*row) + nw, isRow := wgt.(*Row) if isRow { r.Cols = nw.Cols } else { @@ -243,11 +243,11 @@ func NewCol(span, offset int, widgets ...GridBufferer) *row { return r } - r.Cols = []*row{} + r.Cols = []*Row{} ir := r for _, w := range widgets { - nr := &row{Span: 12, Widget: w} - ir.Cols = []*row{nr} + nr := &Row{Span: 12, Widget: w} + ir.Cols = []*Row{nr} ir = nr } From 9d0302382ddeed86cbedb11049dcab10f8131d15 Mon Sep 17 00:00:00 2001 From: gizak Date: Tue, 5 May 2015 11:19:08 -0400 Subject: [PATCH 13/13] Fix Grid test --- grid_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid_test.go b/grid_test.go index 3230033..cdafb20 100644 --- a/grid_test.go +++ b/grid_test.go @@ -10,7 +10,7 @@ import ( "github.com/davecgh/go-spew/spew" ) -var r *row +var r *Row func TestRowWidth(t *testing.T) { p0 := NewPar("p0")