From 894dc0868ea12f479723b45087b4ef7dd83dce9c Mon Sep 17 00:00:00 2001 From: Trevor Slocum Date: Thu, 16 Jan 2020 16:59:14 -0800 Subject: [PATCH] Add ProgressBar widget --- demos/progressbar/main.go | 57 ++++++++++++ progressbar.go | 176 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 demos/progressbar/main.go create mode 100644 progressbar.go diff --git a/demos/progressbar/main.go b/demos/progressbar/main.go new file mode 100644 index 0000000..72305dd --- /dev/null +++ b/demos/progressbar/main.go @@ -0,0 +1,57 @@ +// Demo code for the ProgressBar primitive. +package main + +import ( + "time" + + "github.com/rivo/tview" +) + +func main() { + app := tview.NewApplication() + + grid := tview.NewGrid().SetColumns(-1, 6, 4, 30, -1).SetRows(-1, 12, 4, 4, -1) + grid.SetBackgroundColor(tview.Styles.PrimitiveBackgroundColor) + + verticalProgressBar := tview.NewProgressBar() + verticalProgressBar.SetBorder(true) + verticalProgressBar.SetVertical(true) + + horizontalProgressBar := tview.NewProgressBar() + horizontalProgressBar.SetBorder(true) + horizontalProgressBar.SetMax(150) + + padding := tview.NewTextView() + grid.AddItem(padding, 0, 0, 1, 5, 0, 0, false) + grid.AddItem(padding, 1, 0, 1, 1, 0, 0, false) + grid.AddItem(verticalProgressBar, 1, 1, 2, 1, 0, 0, false) + grid.AddItem(padding, 1, 2, 1, 1, 0, 0, false) + grid.AddItem(padding, 2, 0, 1, 5, 0, 0, false) + grid.AddItem(horizontalProgressBar, 3, 3, 1, 1, 0, 0, false) + grid.AddItem(padding, 1, 4, 1, 1, 0, 0, false) + grid.AddItem(padding, 4, 0, 1, 5, 0, 0, false) + + go func() { + t := time.NewTicker(100 * time.Millisecond) + for range t.C { + if verticalProgressBar.Complete() { + verticalProgressBar.SetProgress(0) + } else { + verticalProgressBar.AddProgress(1) + } + + if horizontalProgressBar.Complete() { + horizontalProgressBar.SetProgress(0) + } else { + horizontalProgressBar.AddProgress(1) + } + + // Queue draw + app.QueueUpdateDraw(func() {}) + } + }() + + if err := app.SetRoot(grid, true).Run(); err != nil { + panic(err) + } +} diff --git a/progressbar.go b/progressbar.go new file mode 100644 index 0000000..0f9da0b --- /dev/null +++ b/progressbar.go @@ -0,0 +1,176 @@ +package tview + +import ( + "math" + "sync" + + "github.com/gdamore/tcell" +) + +// ProgressBar indicates the progress of an operation. +type ProgressBar struct { + *Box + + // Rune to use when rendering the empty area of the progress bar. + emptyRune rune + + // Color of the empty area of the progress bar. + emptyColor tcell.Color + + // Rune to use when rendering the filled area of the progress bar. + filledRune rune + + // Color of the filled area of the progress bar. + filledColor tcell.Color + + // If set to true, instead of filling from left to right, the bar is filled + // from bottom to top. + vertical bool + + // Current progress. + progress int + + // Progress required to fill the bar. + max int + + sync.Mutex +} + +// NewProgressBar returns a new progress bar. +func NewProgressBar() *ProgressBar { + return &ProgressBar{ + Box: NewBox().SetBackgroundColor(Styles.PrimitiveBackgroundColor), + emptyRune: ' ', + emptyColor: Styles.PrimitiveBackgroundColor, + filledRune: tcell.RuneBlock, + filledColor: Styles.PrimaryTextColor, + max: 100, + } +} + +// SetEmptyRune sets the rune used for the empty area of the progress bar. +func (p *ProgressBar) SetEmptyRune(empty rune) { + p.Lock() + defer p.Unlock() + + p.emptyRune = empty +} + +// SetEmptyColor sets the color of the empty area of the progress bar. +func (p *ProgressBar) SetEmptyColor(empty tcell.Color) { + p.Lock() + defer p.Unlock() + + p.emptyColor = empty +} + +// SetFilledRune sets the rune used for the filled area of the progress bar. +func (p *ProgressBar) SetFilledRune(filled rune) { + p.Lock() + defer p.Unlock() + + p.filledRune = filled +} + +// SetFilledColor sets the color of the filled area of the progress bar. +func (p *ProgressBar) SetFilledColor(filled tcell.Color) { + p.Lock() + defer p.Unlock() + + p.filledColor = filled +} + +// SetVertical sets the direction of the progress bar. +func (p *ProgressBar) SetVertical(vertical bool) { + p.Lock() + defer p.Unlock() + + p.vertical = vertical +} + +// SetMax sets the progress required to fill the bar. +func (p *ProgressBar) SetMax(max int) { + p.Lock() + defer p.Unlock() + + p.max = max +} + +// GetMax returns the progress required to fill the bar. +func (p *ProgressBar) GetMax() int { + p.Lock() + defer p.Unlock() + + return p.max +} + +// AddProgress adds to the current progress. +func (p *ProgressBar) AddProgress(progress int) { + p.Lock() + defer p.Unlock() + + p.progress += progress +} + +// SetProgress sets the current progress. +func (p *ProgressBar) SetProgress(progress int) { + p.Lock() + defer p.Unlock() + + p.progress = progress +} + +// GetProgress gets the current progress. +func (p *ProgressBar) GetProgress() int { + p.Lock() + defer p.Unlock() + + return p.progress +} + +// Complete returns whether the progress bar has been filled. +func (p *ProgressBar) Complete() bool { + p.Lock() + defer p.Unlock() + + return p.progress >= p.max +} + +// Draw draws this primitive onto the screen. +func (p *ProgressBar) Draw(screen tcell.Screen) { + p.Box.Draw(screen) + + p.Lock() + defer p.Unlock() + + x, y, width, height := p.GetInnerRect() + + barSize := height + maxLength := width + if p.vertical { + barSize = width + maxLength = height + } + + barLength := int(math.RoundToEven(float64(maxLength) * (float64(p.progress) / float64(p.max)))) + if barLength > maxLength { + barLength = maxLength + } + + for i := 0; i < barSize; i++ { + for j := 0; j < barLength; j++ { + if p.vertical { + screen.SetContent(x+i, y+(height-1-j), p.filledRune, nil, tcell.StyleDefault.Foreground(p.filledColor).Background(p.backgroundColor)) + } else { + screen.SetContent(x+j, y+i, p.filledRune, nil, tcell.StyleDefault.Foreground(p.filledColor).Background(p.backgroundColor)) + } + } + for j := barLength; j < maxLength; j++ { + if p.vertical { + screen.SetContent(x+i, y+(height-1-j), p.emptyRune, nil, tcell.StyleDefault.Foreground(p.emptyColor).Background(p.backgroundColor)) + } else { + screen.SetContent(x+j, y+i, p.emptyRune, nil, tcell.StyleDefault.Foreground(p.emptyColor).Background(p.backgroundColor)) + } + } + } +}