diff --git a/bar.go b/bar.go index ef883de..a53aa61 100644 --- a/bar.go +++ b/bar.go @@ -31,6 +31,9 @@ type Bar struct { priority int index int + + // pointer to running bar, which this bar should replace + waitBar *Bar // completed is set from master Progress goroutine only completed bool @@ -60,6 +63,7 @@ trimRightSpace bool toComplete bool dynamic bool + noBarOnComplete bool startTime time.Time timeElapsed time.Duration blockStartTime time.Time @@ -73,6 +77,7 @@ // following options are assigned to the *Bar priority int removeOnComplete bool + waitBar *Bar } refill struct { char rune @@ -110,9 +115,14 @@ b := &Bar{ priority: s.priority, removeOnComplete: s.removeOnComplete, + waitBar: s.waitBar, operateState: make(chan func(*bState)), done: make(chan struct{}), shutdown: make(chan struct{}), + } + + if b.waitBar != nil { + b.priority = b.waitBar.priority } go b.serve(wg, s, cancel) @@ -322,6 +332,8 @@ } func (s *bState) draw(termWidth int, pSyncer, aSyncer *widthSyncer) io.Reader { + defer s.bufA.WriteByte('\n') + if termWidth <= 0 { termWidth = s.width } @@ -333,21 +345,16 @@ s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i])) } - if !s.trimLeftSpace { - s.bufP.WriteByte(' ') - } - - // render append functions to the right of the bar - if !s.trimRightSpace { - s.bufA.WriteByte(' ') - } - for i, f := range s.aDecorators { s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i])) } prependCount := utf8.RuneCount(s.bufP.Bytes()) appendCount := utf8.RuneCount(s.bufA.Bytes()) + + if s.toComplete && s.noBarOnComplete { + return io.MultiReader(s.bufP, s.bufA) + } s.fillBar(s.width) barCount := utf8.RuneCount(s.bufB.Bytes()) @@ -356,12 +363,19 @@ s.fillBar(termWidth - prependCount - appendCount) } - s.bufA.WriteByte('\n') return io.MultiReader(s.bufP, s.bufB, s.bufA) } func (s *bState) fillBar(width int) { + defer func() { + if !s.trimRightSpace { + s.bufB.WriteByte(' ') + } + }() s.bufB.Reset() + if !s.trimLeftSpace { + s.bufB.WriteByte(' ') + } s.bufB.WriteRune(s.runes[rLeft]) if width <= 2 { s.bufB.WriteRune(s.runes[rRight]) diff --git a/bar_option.go b/bar_option.go index 54b3f8e..fb62f29 100644 --- a/bar_option.go +++ b/bar_option.go @@ -1,6 +1,8 @@ package mpb -import "github.com/vbauerster/mpb/decor" +import ( + "github.com/vbauerster/mpb/decor" +) // BarOption is a function option which changes the default behavior of a bar, // if passed to p.AddBar(int64, ...BarOption) @@ -75,7 +77,7 @@ } } -// BarRemoveOnComplete is a flag, which tells whether the intention is to remove the bar after completion. +// BarRemoveOnComplete is a flag, which will trigger bar auto remove on completion event. func BarRemoveOnComplete() BarOption { return func(s *bState) { s.removeOnComplete = true @@ -84,9 +86,24 @@ // BarPriority sets bar's priority. // Zero is highest priority, i.e. bar will be on top. +// If `BarReplaceOnComplete` option is supplied, this option is ignored. func BarPriority(priority int) BarOption { return func(s *bState) { s.priority = priority + } +} + +// BarReplaceOnComplete provided `b` is usually already running bar which this bar should replace. +func BarReplaceOnComplete(b *Bar) BarOption { + return func(s *bState) { + s.waitBar = b + } +} + +// BarClearOnComplete clears the bar section on complete event. +func BarClearOnComplete() BarOption { + return func(s *bState) { + s.noBarOnComplete = true } } diff --git a/progress.go b/progress.go index 0b31d6d..bc2c012 100644 --- a/progress.go +++ b/progress.go @@ -46,6 +46,7 @@ cancel <-chan struct{} shutdownNotifier chan struct{} interceptors []func(io.Writer) + waitBars map[*Bar]*Bar } widthSyncer struct { // Public for easy testing @@ -60,12 +61,13 @@ pq := make(priorityQueue, 0) heap.Init(&pq) s := &pState{ - bHeap: &pq, - width: pwidth, - format: pformat, - cw: cwriter.New(os.Stdout), - rr: prr, - ticker: time.NewTicker(prr), + bHeap: &pq, + width: pwidth, + format: pformat, + cw: cwriter.New(os.Stdout), + rr: prr, + ticker: time.NewTicker(prr), + waitBars: make(map[*Bar]*Bar), } for _, opt := range options { @@ -92,8 +94,12 @@ case p.operateState <- func(s *pState) { options = append(options, barWidth(s.width), barFormat(s.format)) b := newBar(p.wg, s.idCounter, total, s.cancel, options...) - heap.Push(s.bHeap, b) - s.heapUpdated = true + if b.waitBar != nil { + s.waitBars[b.waitBar] = b + } else { + heap.Push(s.bHeap, b) + s.heapUpdated = true + } s.idCounter++ result <- b }: @@ -208,6 +214,11 @@ if rs.bar.removeOnComplete { s.heapUpdated = heap.Remove(s.bHeap, rs.bar.index) != nil } + if replacementBar, ok := s.waitBars[rs.bar]; ok { + heap.Push(s.bHeap, replacementBar) + s.heapUpdated = true + delete(s.waitBars, rs.bar) + } defer func() { s.shutdownPending = append(s.shutdownPending, rs.bar) }()