clui/progressbar.go

234 lines
5.9 KiB
Go

package clui
import (
xs "github.com/huandu/xstrings"
term "github.com/nsf/termbox-go"
"strconv"
"strings"
)
/*
ProgressBar control visualizes the progression of extended operation.
The control has two sets of colors(almost all other controls have only
one set: foreground and background colors): for filled part and for
empty one. By default colors are the same.
*/
type ProgressBar struct {
BaseControl
direction Direction
min, max int
value int
emptyFg, emptyBg term.Attribute
titleFg term.Attribute
}
/*
NewProgressBar creates a new ProgressBar.
parent - is container that keeps the control.
width and heigth - are minimal size of the control.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func CreateProgressBar(parent Control, width, height int, scale int) *ProgressBar {
b := new(ProgressBar)
if height == AutoSize {
height = 1
}
if width == AutoSize {
width = 10
}
b.SetSize(width, height)
b.SetConstraints(width, height)
b.SetTabStop(false)
b.SetScale(scale)
b.min = 0
b.max = 10
b.direction = Horizontal
b.parent = parent
b.align = AlignCenter
if parent != nil {
parent.AddChild(b)
}
return b
}
// Repaint draws the control on its View surface.
// Horizontal ProgressBar supports custom title over the bar.
// One can set title using method SetTitle. There are a few
// predefined variables that can be used inside title to
// show value or total progress. Variable must be enclosed
// with double curly brackets. Available variables:
// percent - the current percentage rounded to closest integer
// value - raw ProgressBar value
// min - lower ProgressBar limit
// max - upper ProgressBar limit
// Examples:
// pb.SetTitle("{{value}} of {{max}}")
// pb.SetTitle("{{percent}}%")
func (b *ProgressBar) Draw() {
if b.max <= b.min {
return
}
PushAttributes()
defer PopAttributes()
fgOff, fgOn := RealColor(b.fg, ColorProgressText), RealColor(b.fgActive, ColorProgressActiveText)
bgOff, bgOn := RealColor(b.bg, ColorProgressBack), RealColor(b.bgActive, ColorProgressActiveBack)
parts := []rune(SysObject(ObjProgressBar))
cFilled, cEmpty := parts[0], parts[1]
prc := 0
if b.value >= b.max {
prc = 100
} else if b.value < b.max && b.value > b.min {
prc = (100 * (b.value - b.min)) / (b.max - b.min)
}
var title string
if b.direction == Horizontal && b.Title() != "" {
title = b.Title()
title = strings.Replace(title, "{{percent}}", strconv.Itoa(prc), -1)
title = strings.Replace(title, "{{value}}", strconv.Itoa(b.value), -1)
title = strings.Replace(title, "{{min}}", strconv.Itoa(b.min), -1)
title = strings.Replace(title, "{{max}}", strconv.Itoa(b.max), -1)
}
x, y := b.Pos()
w, h := b.Size()
if b.direction == Horizontal {
filled := prc * w / 100
sFilled := strings.Repeat(string(cFilled), filled)
sEmpty := strings.Repeat(string(cEmpty), w-filled)
for yy := y; yy < y+h; yy++ {
SetTextColor(fgOn)
SetBackColor(bgOn)
DrawRawText(x, yy, sFilled)
SetTextColor(fgOff)
SetBackColor(bgOff)
DrawRawText(x+filled, yy, sEmpty)
}
if title != "" {
shift, str := AlignText(title, w, b.align)
titleClr := RealColor(b.titleFg, ColorProgressTitleText)
var sOn, sOff string
if filled == 0 || shift >= filled {
sOff = str
} else if w == filled || shift+xs.Len(str) < filled {
sOn = str
} else {
r := filled - shift
sOn = xs.Slice(str, 0, r)
sOff = xs.Slice(str, r, -1)
}
SetTextColor(titleClr)
if sOn != "" {
SetBackColor(bgOn)
DrawRawText(x+shift, y, sOn)
}
if sOff != "" {
SetBackColor(bgOff)
DrawRawText(x+shift+xs.Len(sOn), y, sOff)
}
}
} else {
filled := prc * h / 100
sFilled := strings.Repeat(string(cFilled), w)
sEmpty := strings.Repeat(string(cEmpty), w)
for yy := y; yy < y+h-filled; yy++ {
SetTextColor(fgOff)
SetBackColor(bgOff)
DrawRawText(x, yy, sEmpty)
}
for yy := y + h - filled; yy < y+h; yy++ {
SetTextColor(fgOff)
SetBackColor(bgOff)
DrawRawText(x, yy, sFilled)
}
}
}
//----------------- own methods -------------------------
// SetValue sets new progress value. If value exceeds ProgressBar
// limits then the limit value is used
func (b *ProgressBar) SetValue(pos int) {
if pos < b.min {
b.value = b.min
} else if pos > b.max {
b.value = b.max
} else {
b.value = pos
}
}
// Value returns the current ProgressBar value
func (b *ProgressBar) Value() int {
return b.value
}
// Limits returns current minimal and maximal values of ProgressBar
func (b *ProgressBar) Limits() (int, int) {
return b.min, b.max
}
// SetLimits set new ProgressBar limits. The current value
// is adjusted if it exceeds new limits
func (b *ProgressBar) SetLimits(min, max int) {
b.min = min
b.max = max
if b.value < b.min {
b.value = min
}
if b.value > b.max {
b.value = max
}
}
// Step increases ProgressBar value by 1 if the value is less
// than ProgressBar high limit
func (b *ProgressBar) Step() int {
b.value++
if b.value > b.max {
b.value = b.max
}
return b.value
}
// SecondaryColors returns text and background colors for empty
// part of the ProgressBar
func (b *ProgressBar) SecondaryColors() (term.Attribute, term.Attribute) {
return b.emptyFg, b.emptyBg
}
// SetSecondaryColors sets new text and background colors for
// empty part of the ProgressBar
func (b *ProgressBar) SetSecondaryColors(fg, bg term.Attribute) {
b.emptyFg, b.emptyBg = fg, bg
}
// TitleColor returns text color of ProgressBar's title. Title
// background color always equals background color of the
// part of the ProgressBar on which it is displayed. In other
// words, background color of title is transparent
func (b *ProgressBar) TitleColor() term.Attribute {
return b.titleFg
}
// SetTitleColor sets text color of ProgressBar's title
func (b *ProgressBar) SetTitleColor(clr term.Attribute) {
b.titleFg = clr
}