Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions _examples/horizontal_stacked_barchart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. 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 (
"log"

ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
)

func main() {
if err := ui.Init(); err != nil {
log.Fatalf("failed to initialize termui: %v", err)
}
defer ui.Close()

hsbc := widgets.NewHorizontalStackedBar()
hsbc.Title = "Student's Marks: X-Axis=Grade% (Math, English, Science, Computer Science), Y-Axis=Name"
hsbc.Labels = []string{"Ken", "Rob", "Dennis", "Linus"}

hsbc.Data = make([][]float64, 4)
hsbc.Data[0] = []float64{90, 85, 90, 80}
hsbc.Data[1] = []float64{70, 85, 75, 60}
hsbc.Data[2] = []float64{75, 60, 80, 85}
hsbc.Data[3] = []float64{100, 100, 100, 100}
hsbc.SetRect(5, 5, 100, 30)
hsbc.BarHeight = 3

ui.Render(hsbc)

uiEvents := ui.PollEvents()
for {
e := <-uiEvents
switch e.ID {
case "q", "<C-c>":
return
}
}
}
94 changes: 94 additions & 0 deletions widgets/horizontal_stacked_barchart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package widgets

import (
"fmt"
"image"

rw "github.com/mattn/go-runewidth"

. "github.com/gizak/termui/v3"
)

type HorizontalStackedBar struct {
Block
BarColors []Color
LabelStyles []Style
NumStyles []Style // only Fg and Modifier are used
NumFormatter func(float64) string
Data [][]float64
Labels []string
BarHeight int
BarGap int
MaxVal float64
}

func NewHorizontalStackedBar() *HorizontalStackedBar {
return &HorizontalStackedBar{
Block: *NewBlock(),
BarColors: Theme.StackedBarChart.Bars,
LabelStyles: Theme.StackedBarChart.Labels,
NumStyles: Theme.StackedBarChart.Nums,
NumFormatter: func(n float64) string { return fmt.Sprint(n) },
BarGap: 2,
BarHeight: 3,
}
}

func (self *HorizontalStackedBar) Draw(buf *Buffer) {
self.Block.Draw(buf)

maxVal := self.MaxVal
if maxVal == 0 {
for _, data := range self.Data {
maxVal = MaxFloat64(maxVal, SumFloat64Slice(data))
}
}

barYCoordinate := self.Inner.Min.Y+1

labelWidth := 10

for i, bar := range self.Data {
stackX := labelWidth

for j, data := range bar {
width := int((data / maxVal) * float64(self.Inner.Dx()-labelWidth-1))

for y := barYCoordinate; y < MinInt(barYCoordinate+self.BarHeight, self.Inner.Max.Y); y++ {
for x := self.Inner.Min.X + stackX; x < MinInt(self.Inner.Min.X+stackX+width, self.Inner.Max.X); x++ {
c := NewCell(' ', NewStyle(ColorClear, SelectColor(self.BarColors, j)))
buf.SetCell(c, image.Pt(x, y))
}
}

textX := self.Inner.Min.X + stackX + width/2 - rw.StringWidth(self.NumFormatter(data))/2
textY := barYCoordinate+self.BarHeight/2
buf.SetString(
self.NumFormatter(data),
NewStyle(
SelectStyle(self.NumStyles, j+1).Fg,
ColorClear,
SelectStyle(self.NumStyles, j+1).Modifier,
),
image.Pt(textX, textY),
)

stackX += width
}

if i < len(self.Labels) {
labelY := barYCoordinate+self.BarHeight/2
buf.SetString(
TrimString(self.Labels[i], labelWidth-2),
SelectStyle(self.LabelStyles, i),
image.Pt(self.Inner.Min.X+1, labelY),
)
}

barYCoordinate += (self.BarHeight + self.BarGap)
}
}