tvxwidgets/barchart.go
2021-12-20 21:02:43 +11:00

197 lines
4.7 KiB
Go

package tgraph
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
const (
barChartYAxisLabelWidth = 2
)
// BarChartItem represents a single bar in bar chart
type BarChartItem struct {
label string
value int
color tcell.Color
}
// BarChart represents bar chart primitive.
type BarChart struct {
*tview.Box
// bar items
bars []BarChartItem
// maximum value of bars
maxVal int
// barGap gap between two bars
barGap int
// barWidth width of bars
barWidth int
//hasBorder true if primitive has border
hasBorder bool
}
// NewBarChart returns a new bar chart primitive.
func NewBarChart() *BarChart {
chart := &BarChart{
Box: tview.NewBox(),
barGap: 2,
barWidth: 3,
}
return chart
}
// Focus is called when this primitive receives focus
func (c *BarChart) Focus(delegate func(p tview.Primitive)) {
delegate(c.Box)
}
// HasFocus returns whether or not this primitive has focus.
func (c *BarChart) HasFocus() bool {
return c.Box.HasFocus()
}
// Draw draws this primitive onto the screen.
func (c *BarChart) Draw(screen tcell.Screen) {
style := tcell.StyleDefault
style = style.Foreground(tview.Styles.BorderColor).Background(tview.Styles.PrimitiveBackgroundColor)
c.Box.DrawForSubclass(screen, c)
x, y, width, height := c.Box.GetInnerRect()
maxValY := y + 1
xAxisStartY := height - 2
barStartY := height - 3
borderPadding := 0
if c.hasBorder {
borderPadding = 1
}
// set max value if not set
c.initMaxValue()
maxValueSr := fmt.Sprintf("%d", c.maxVal)
maxValLenght := len(maxValueSr) + 1
if maxValLenght < barChartYAxisLabelWidth {
maxValLenght = barChartYAxisLabelWidth
}
// draw graph y-axis
for i := borderPadding; i+y < height; i++ {
tview.PrintJoinedSemigraphics(screen, x+maxValLenght, y+i, tview.Borders.Vertical, style)
}
// draw graph x-axix
for i := maxValLenght; i+x < width-borderPadding; i++ {
tview.PrintJoinedSemigraphics(screen, x+i, xAxisStartY, tview.Borders.Horizontal, style)
}
tview.PrintJoinedSemigraphics(screen, x+maxValLenght, xAxisStartY, tview.BoxDrawingsLightVerticalAndRight, style)
tview.PrintJoinedSemigraphics(screen, x+maxValLenght-1, xAxisStartY, '0', style)
mxValRune := []rune(maxValueSr)
for i := 0; i < len(mxValRune); i++ {
tview.PrintJoinedSemigraphics(screen, x+borderPadding+i, maxValY, mxValRune[i], style)
}
// draw bars
startX := x + maxValLenght + c.barGap
labelY := height - 1
valueMaxHeight := barStartY - borderPadding - 1
for _, item := range c.bars {
if startX > width {
return
}
// set labels
r := []rune(item.label)
for j := 0; j < len(r); j++ {
tview.PrintJoinedSemigraphics(screen, startX+j, labelY, r[j], style)
}
// bar style
bStyle := style.Foreground(item.color)
barHeight := c.getHeight(valueMaxHeight, item.value)
for k := 0; k < barHeight; k++ {
for l := 0; l < c.barWidth; l++ {
tview.PrintJoinedSemigraphics(screen, startX+l, barStartY-k, '\u2588', bStyle)
}
}
// bar value
vSt := fmt.Sprintf("%d", item.value)
vRune := []rune(vSt)
for i := 0; i < len(vRune); i++ {
tview.PrintJoinedSemigraphics(screen, startX+i, barStartY-barHeight, vRune[i], bStyle)
}
// calculate next startX for next bar
rWidth := len(r)
if rWidth < c.barWidth {
rWidth = c.barWidth
}
startX = startX + c.barGap + rWidth
}
}
// SetBorder sets border for this primitive
func (c *BarChart) SetBorder(status bool) {
c.hasBorder = status
c.Box.SetBorder(status)
}
func (c *BarChart) GetRect() (int, int, int, int) {
return c.Box.GetRect()
}
// SetRect sets rect for this primitive.
func (c *BarChart) SetRect(x, y, width, height int) {
c.Box.SetRect(x, y, width, height)
}
// InputHandler returns input handler function for this primitive
func (c *BarChart) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return c.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
})
}
// SetMaxValue sets maximum value of bars
func (c *BarChart) SetMaxValue(max int) {
c.maxVal = max
}
// AddBar adds new bar item to the bar chart primitive.
func (c *BarChart) AddBar(label string, value int, color tcell.Color) {
c.bars = append(c.bars, BarChartItem{
label: label,
value: value,
color: color,
})
}
// SetBarValue sets bar values
func (c *BarChart) SetBarValue(name string, value int) {
for i := 0; i < len(c.bars); i++ {
if c.bars[i].label == name {
c.bars[i].value = value
}
}
}
func (c *BarChart) getHeight(maxHeight int, value int) int {
height := 0
if value > c.maxVal {
return maxHeight
}
height = (value * maxHeight) / c.maxVal
return height
}
func (c *BarChart) initMaxValue() {
// set max value if not set
if c.maxVal == 0 {
for _, b := range c.bars {
if b.value > c.maxVal {
c.maxVal = b.value
}
}
}
}