diff --git a/bar.go b/bar.go index 5828984..0c487a4 100644 --- a/bar.go +++ b/bar.go @@ -43,27 +43,28 @@ type ( bState struct { - id int - width int - format fmtRunes - etaAlpha float64 - total int64 - current int64 - dropRatio int64 - trimLeftSpace bool - trimRightSpace bool - completed bool - aborted bool - dynamic bool - startTime time.Time - timeElapsed time.Duration - blockStartTime time.Time - timePerItem time.Duration - appendFuncs []decor.DecoratorFunc - prependFuncs []decor.DecoratorFunc - refill *refill - bufP, bufB, bufA *bytes.Buffer - panic string + id int + width int + format fmtRunes + etaAlpha float64 + total int64 + current int64 + totalAutoIncrTrigger int64 + totalAutoIncrBy int64 + trimLeftSpace bool + trimRightSpace bool + completed bool + aborted bool + dynamic bool + startTime time.Time + timeElapsed time.Duration + blockStartTime time.Time + timePerItem time.Duration + appendFuncs []decor.DecoratorFunc + prependFuncs []decor.DecoratorFunc + refill *refill + bufP, bufB, bufA *bytes.Buffer + panic string } refill struct { char rune @@ -81,10 +82,10 @@ } s := &bState{ - id: id, - total: total, - etaAlpha: etaAlpha, - dropRatio: 10, + id: id, + total: total, + etaAlpha: etaAlpha, + totalAutoIncrTrigger: 10, } for _, opt := range options { @@ -153,8 +154,9 @@ } s.current += int64(n) if s.dynamic { - for s.current >= s.total { - s.current -= s.current * s.dropRatio / 100 + curp := decor.CalcPercentage(s.total, s.current, 100) + if 100-curp <= s.totalAutoIncrTrigger { + s.total += s.totalAutoIncrBy } } else if s.current >= s.total { s.current = s.total @@ -389,30 +391,32 @@ // bar s.width without leftEnd and rightEnd runes barWidth := width - 2 - completedWidth := decor.CalcPercentage(s.total, s.current, barWidth) + completedWidth := decor.CalcPercentage(s.total, s.current, int64(barWidth)) if s.refill != nil { - till := decor.CalcPercentage(s.total, s.refill.till, barWidth) + till := decor.CalcPercentage(s.total, s.refill.till, int64(barWidth)) // append refill rune - for i := 0; i < till; i++ { + var i int64 + for i = 0; i < till; i++ { s.bufB.WriteRune(s.refill.char) } - for i := till; i < completedWidth; i++ { + for i = till; i < completedWidth; i++ { s.bufB.WriteRune(s.format[rFill]) } } else { - for i := 0; i < completedWidth; i++ { + var i int64 + for i = 0; i < completedWidth; i++ { s.bufB.WriteRune(s.format[rFill]) } } - if completedWidth < barWidth && completedWidth > 0 { + if completedWidth < int64(barWidth) && completedWidth > 0 { _, size := utf8.DecodeLastRune(s.bufB.Bytes()) s.bufB.Truncate(s.bufB.Len() - size) s.bufB.WriteRune(s.format[rTip]) } - for i := completedWidth; i < barWidth; i++ { + for i := completedWidth; i < int64(barWidth); i++ { s.bufB.WriteRune(s.format[rEmpty]) } diff --git a/bar_option.go b/bar_option.go index 5c0a126..9dbd272 100644 --- a/bar_option.go +++ b/bar_option.go @@ -58,12 +58,20 @@ } } -// BarDropRatio sets drop ratio, default is 10. Effective when total is dynamic. -// If progress tip reaches total, but total is not final value yet, tip will be -// dropped by specified ratio. -func BarDropRatio(ratio int64) BarOption { +// BarDynamicTotal enables dynamic total behaviour. +func BarDynamicTotal() BarOption { return func(s *bState) { - s.dropRatio = ratio + s.dynamic = true + } +} + +// BarAutoIncrTotal auto increment total by amount, when trigger percentage remained till bar completion. +// In other words: say you've set trigger = 10, then auto increment will start after bar reaches 90 %. +func BarAutoIncrTotal(trigger, amount int64) BarOption { + return func(s *bState) { + s.dynamic = true + s.totalAutoIncrTrigger = trigger + s.totalAutoIncrBy = amount } } diff --git a/decor/decorators.go b/decor/decorators.go index 0d0c2ba..d442e55 100644 --- a/decor/decorators.go +++ b/decor/decorators.go @@ -204,7 +204,7 @@ } } -func CalcPercentage(total, current int64, width int) int { +func CalcPercentage(total, current, width int64) int64 { if total == 0 || current > total { return 0 } @@ -214,7 +214,7 @@ // num = 2.34 will return 2 // num = 2.44 will return 3 if math.Max(diff, 0.6) == diff { - return int(num) - } - return int(ceil) -} + return int64(num) + } + return int64(ceil) +} diff --git a/examples/dynTotal/main.go b/examples/dynTotal/main.go new file mode 100644 index 0000000..6f38a17 --- /dev/null +++ b/examples/dynTotal/main.go @@ -0,0 +1,51 @@ +package main + +import ( + "math/rand" + "time" + + "github.com/vbauerster/mpb" + "github.com/vbauerster/mpb/decor" +) + +func main() { + p := mpb.New() + + // initialize bar with dynamic total and initial total guess = 80 + bar := p.AddBar(80, + // indicate that total is dynamic + mpb.BarDynamicTotal(), + // trigger total auto increment by 1, when 18 % remains till bar completion + mpb.BarAutoIncrTotal(18, 1), + mpb.PrependDecorators( + decor.CountersNoUnit("%d / %d", 12, 0), + ), + mpb.AppendDecorators( + decor.Percentage(5, 0), + ), + ) + + totalUpd1 := make(chan struct{}) + totalUpd2 := make(chan struct{}) + go func() { + <-totalUpd1 + // intermediate not final total update + bar.SetTotal(200, false) + <-totalUpd2 + // final total update + bar.SetTotal(300, true) + }() + + for i := 0; i < 300; i++ { + if i == 140 { + close(totalUpd1) + } + if i == 250 { + close(totalUpd2) + } + time.Sleep(time.Duration(rand.Intn(10)+1) * time.Second / 100) + bar.Increment() + } + + p.Stop() +}