clui/progressbar.go

247 lines
6.1 KiB
Go
Raw Normal View History

2015-09-21 20:54:39 -07:00
package clui
import (
2015-11-24 17:28:20 -08:00
xs "github.com/huandu/xstrings"
2015-10-16 10:27:43 -07:00
term "github.com/nsf/termbox-go"
2015-11-24 17:28:20 -08:00
"strconv"
2015-09-21 20:54:39 -07:00
"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
2015-10-16 10:27:43 -07:00
direction Direction
min, max int
value int
emptyFg, emptyBg term.Attribute
2015-11-24 17:28:20 -08:00
titleFg term.Attribute
2015-09-21 20:54:39 -07:00
}
2015-10-29 10:05:41 -07:00
/*
NewProgressBar creates a new ProgressBar.
parent - is container that keeps the control.
2015-10-29 10:36:18 -07:00
width and heigth - are minimal size of the control.
2015-10-29 10:05:41 -07:00
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 {
2015-09-21 20:54:39 -07:00
b := new(ProgressBar)
b.BaseControl = NewBaseControl()
2015-10-20 09:43:56 -07:00
if height == AutoSize {
height = 1
}
if width == AutoSize {
width = 10
}
2015-09-21 20:54:39 -07:00
b.SetSize(width, height)
2015-10-16 10:27:43 -07:00
b.SetConstraints(width, height)
b.SetTabStop(false)
b.SetScale(scale)
2015-10-16 10:27:43 -07:00
b.min = 0
b.max = 10
b.direction = Horizontal
2015-09-21 20:54:39 -07:00
b.parent = parent
2015-11-24 17:28:20 -08:00
b.align = AlignCenter
2015-09-21 20:54:39 -07:00
2015-10-16 10:27:43 -07:00
if parent != nil {
parent.AddChild(b)
2015-09-21 20:54:39 -07:00
}
2015-10-16 10:27:43 -07:00
return b
2015-09-21 20:54:39 -07:00
}
2015-11-24 17:28:20 -08:00
// 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() {
2018-03-13 22:14:40 -07:00
if b.hidden {
return
}
b.mtx.RLock()
defer b.mtx.RUnlock()
2015-09-21 20:54:39 -07:00
if b.max <= b.min {
return
}
PushAttributes()
defer PopAttributes()
2015-09-21 20:54:39 -07:00
fgOff, fgOn := RealColor(b.fg, b.Style(), ColorProgressText), RealColor(b.fgActive, b.Style(), ColorProgressActiveText)
bgOff, bgOn := RealColor(b.bg, b.Style(), ColorProgressBack), RealColor(b.bgActive, b.Style(), ColorProgressActiveBack)
2015-09-21 20:54:39 -07:00
parts := []rune(SysObject(ObjProgressBar))
2015-10-16 10:27:43 -07:00
cFilled, cEmpty := parts[0], parts[1]
2015-09-21 20:54:39 -07:00
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)
}
2015-11-24 17:28:20 -08:00
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)
}
2015-10-16 10:27:43 -07:00
x, y := b.Pos()
w, h := b.Size()
2015-09-21 20:54:39 -07:00
2015-10-16 10:27:43 -07:00
if b.direction == Horizontal {
2015-09-21 20:54:39 -07:00
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)
2015-09-21 20:54:39 -07:00
}
2015-11-24 17:28:20 -08:00
if title != "" {
shift, str := AlignText(title, w, b.align)
titleClr := RealColor(b.titleFg, b.Style(), ColorProgressTitleText)
2015-11-24 17:28:20 -08:00
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)
2015-11-24 17:28:20 -08:00
if sOn != "" {
SetBackColor(bgOn)
DrawRawText(x+shift, y, sOn)
2015-11-24 17:28:20 -08:00
}
if sOff != "" {
SetBackColor(bgOff)
DrawRawText(x+shift+xs.Len(sOn), y, sOff)
2015-11-24 17:28:20 -08:00
}
}
2015-09-21 20:54:39 -07:00
} 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)
2015-09-21 20:54:39 -07:00
}
for yy := y + h - filled; yy < y+h; yy++ {
SetTextColor(fgOff)
SetBackColor(bgOff)
DrawRawText(x, yy, sFilled)
2015-09-21 20:54:39 -07:00
}
}
}
//----------------- own methods -------------------------
2015-10-29 10:05:41 -07:00
// SetValue sets new progress value. If value exceeds ProgressBar
2015-09-21 20:54:39 -07:00
// limits then the limit value is used
func (b *ProgressBar) SetValue(pos int) {
2018-03-13 22:14:40 -07:00
b.mtx.Lock()
defer b.mtx.Unlock()
2015-09-21 20:54:39 -07:00
if pos < b.min {
b.value = b.min
} else if pos > b.max {
b.value = b.max
} else {
b.value = pos
}
}
2015-10-29 10:05:41 -07:00
// Value returns the current ProgressBar value
2015-10-16 10:27:43 -07:00
func (b *ProgressBar) Value() int {
2018-03-13 22:14:40 -07:00
b.mtx.RLock()
defer b.mtx.RUnlock()
2015-10-16 10:27:43 -07:00
return b.value
}
2015-10-29 10:05:41 -07:00
// Limits returns current minimal and maximal values of ProgressBar
2015-10-16 10:27:43 -07:00
func (b *ProgressBar) Limits() (int, int) {
return b.min, b.max
}
2015-10-29 10:05:41 -07:00
// SetLimits set new ProgressBar limits. The current value
// is adjusted if it exceeds new limits
2015-09-21 20:54:39 -07:00
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
}
}
2015-10-29 10:05:41 -07:00
// Step increases ProgressBar value by 1 if the value is less
2015-09-21 20:54:39 -07:00
// than ProgressBar high limit
func (b *ProgressBar) Step() int {
2018-03-13 22:14:40 -07:00
b.mtx.Lock()
defer b.mtx.Unlock()
2015-09-21 20:54:39 -07:00
b.value++
if b.value > b.max {
b.value = b.max
}
return b.value
}
2015-10-29 10:05:41 -07:00
// SecondaryColors returns text and background colors for empty
// part of the ProgressBar
2015-10-16 10:27:43 -07:00
func (b *ProgressBar) SecondaryColors() (term.Attribute, term.Attribute) {
return b.emptyFg, b.emptyBg
2015-09-21 20:54:39 -07:00
}
2015-10-29 10:05:41 -07:00
// SetSecondaryColors sets new text and background colors for
// empty part of the ProgressBar
2015-10-16 10:27:43 -07:00
func (b *ProgressBar) SetSecondaryColors(fg, bg term.Attribute) {
b.emptyFg, b.emptyBg = fg, bg
2015-09-21 20:54:39 -07:00
}
2015-11-24 17:28:20 -08:00
// 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
}