1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-25 13:48:50 +08:00

add gauge threshold

This commit is contained in:
Lovro Mažgon 2022-12-08 14:49:38 +01:00
parent 9954a0543e
commit 1aba280365
No known key found for this signature in database
GPG Key ID: A8554FEB34E8BC79
4 changed files with 140 additions and 5 deletions

View File

@ -136,10 +136,12 @@ func (g *Gauge) Percent(p int, opts ...Option) error {
return nil
}
// width determines the required width of the gauge drawn on the provided area
// in order to represent the current progress.
func (g *Gauge) width(ar image.Rectangle) int {
mult := float32(g.current) / float32(g.total)
// width determines the X coordinate that represents point w in rectangle ar.
// This is used to calculate the width of the gauge drawn on the provided area
// in order to represent the current progress or to figure out the coordinate
// for the threshold line.
func (g *Gauge) width(ar image.Rectangle, w int) int {
mult := float32(w) / float32(g.total)
width := float32(ar.Dx()) * mult
return int(width)
}
@ -273,7 +275,7 @@ func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
progress := image.Rect(
usable.Min.X,
usable.Min.Y,
usable.Min.X+g.width(usable),
usable.Min.X+g.width(usable, g.current),
usable.Max.Y,
)
if progress.Dx() > 0 {
@ -284,6 +286,25 @@ func (g *Gauge) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
return err
}
}
if g.opts.threshold > 0 && g.opts.threshold < g.total {
line := draw.HVLine{
Start: image.Point{
X: usable.Min.X + g.width(usable, g.opts.threshold),
Y: cvs.Area().Min.Y,
},
End: image.Point{
X: usable.Min.X + g.width(usable, g.opts.threshold),
Y: cvs.Area().Max.Y - 1,
},
}
if err := draw.HVLines(cvs, []draw.HVLine{line},
draw.HVLineStyle(g.opts.thresholdLineStyle),
draw.HVLineCellOpts(g.opts.thresholdCellOpts...),
); err != nil {
return err
}
}
return g.drawText(cvs, progress)
}

View File

@ -841,6 +841,100 @@ func TestGauge(t *testing.T) {
return ft
},
},
{
desc: "threshold with border percentage",
opts: []Option{
Char('o'),
Threshold(20, linestyle.Double),
},
percent: &percentCall{p: 35},
canvas: image.Rect(0, 0, 10, 3),
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
c := testcanvas.MustNew(ft.Area())
testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3),
draw.RectChar('o'),
draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
)
testdraw.MustText(c, "35%", image.Point{3, 1})
testdraw.MustHVLines(c, []draw.HVLine{{
Start: image.Point{X: 2, Y: 0},
End: image.Point{X: 2, Y: 2},
}}, draw.HVLineStyle(linestyle.Double))
testcanvas.MustApply(c, ft)
return ft
},
},
{
desc: "threshold without border absolute",
opts: []Option{
Char('o'),
Threshold(3, linestyle.Light, cell.BgColor(cell.ColorRed)),
Border(linestyle.None),
HideTextProgress(),
},
absolute: &absoluteCall{done: 4, total: 10},
canvas: image.Rect(0, 0, 10, 3),
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
c := testcanvas.MustNew(ft.Area())
testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3),
draw.RectChar('o'),
draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
)
testdraw.MustHVLines(c, []draw.HVLine{{
Start: image.Point{X: 3, Y: 0},
End: image.Point{X: 3, Y: 2},
}}, draw.HVLineStyle(linestyle.Light),
draw.HVLineCellOpts(cell.BgColor(cell.ColorRed)))
testcanvas.MustApply(c, ft)
return ft
},
},
{
desc: "threshold outside of bounds (negative)",
opts: []Option{
Char('o'),
HideTextProgress(),
Threshold(-1, linestyle.Light), // ignored
},
percent: &percentCall{p: 35},
canvas: image.Rect(0, 0, 10, 3),
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
c := testcanvas.MustNew(ft.Area())
testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3),
draw.RectChar('o'),
draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
)
testcanvas.MustApply(c, ft)
return ft
},
},
{
desc: "threshold outside of bounds (>=max)",
opts: []Option{
Char('o'),
HideTextProgress(),
Threshold(100, linestyle.Light), // ignored
},
percent: &percentCall{p: 35},
canvas: image.Rect(0, 0, 10, 3),
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
c := testcanvas.MustNew(ft.Area())
testdraw.MustRectangle(c, image.Rect(0, 0, 3, 3),
draw.RectChar('o'),
draw.RectCellOpts(cell.BgColor(cell.ColorGreen)),
)
testcanvas.MustApply(c, ft)
return ft
},
},
}
for _, tc := range tests {

View File

@ -101,6 +101,7 @@ func main() {
gauge.Color(cell.ColorNumber(33)),
gauge.Border(linestyle.Light),
gauge.BorderTitle("Absolute progress"),
gauge.Threshold(43, linestyle.Light, cell.FgColor(cell.ColorRed)),
)
if err != nil {
panic(err)
@ -124,6 +125,7 @@ func main() {
gauge.Color(cell.ColorRed),
gauge.FilledTextColor(cell.ColorBlack),
gauge.EmptyTextColor(cell.ColorYellow),
gauge.Threshold(20, linestyle.Double),
)
if err != nil {
panic(err)

View File

@ -47,6 +47,10 @@ type options struct {
borderCellOpts []cell.Option
borderTitle string
borderTitleHAlign align.Horizontal
// If set draws a vertical line representing the threshold.
threshold int
thresholdCellOpts []cell.Option
thresholdLineStyle linestyle.LineStyle
}
// newOptions returns options with the default values set.
@ -201,3 +205,17 @@ func BorderTitleAlign(h align.Horizontal) Option {
opts.borderTitleHAlign = h
})
}
// Threshold configures the Gauge to display a vertical threshold line at value
// t. If the progress is set by a call to Percent(), t represents a percentage,
// e.g. "40" means line is displayed at 40%. If the progress is set by a call to
// Absolute(), the threshold is also considered an absolute number.
// Threshold needs to be configured inside the valid values of a gauge to be
// displayed, if it's set to <=0 or >=total it won't have any effect.
func Threshold(t int, ls linestyle.LineStyle, cOpts ...cell.Option) Option {
return option(func(opts *options) {
opts.threshold = t
opts.thresholdLineStyle = ls
opts.thresholdCellOpts = cOpts
})
}