package tui import ( "context" "fmt" "sync/atomic" "time" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) type LoadingBar struct { *tview.Box view *tview.TextView secondsElapsed atomic.Int64 needDrawFunc func() reset func() } func NewLoadingBar(needDrawFunc func()) *LoadingBar { b := &LoadingBar{ Box: tview.NewBox(), view: tview.NewTextView(), needDrawFunc: needDrawFunc, } b.view.SetBackgroundColor(tview.Styles.PrimaryTextColor) b.view.SetTextColor(b.GetBackgroundColor()) return b } func (b *LoadingBar) Start(ctx context.Context) { ctx, b.reset = context.WithCancel(ctx) go func() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() b.secondsElapsed.Store(0) for { select { case <-ctx.Done(): return case <-ticker.C: b.secondsElapsed.Add(1) b.needDrawFunc() } } }() } func (b *LoadingBar) Stop() { b.reset() } func (b *LoadingBar) Draw(screen tcell.Screen) { seconds := b.secondsElapsed.Load() var time string switch { case seconds < 60: time = fmt.Sprintf("%ds", seconds) default: time = fmt.Sprintf("%dm%ds", seconds/60, seconds%60) } b.view.SetText(fmt.Sprintf(" Loading... %s (press Escape to cancel) ", time)) x, y, width, _ := b.GetInnerRect() b.view.SetRect(x, y, width, 1) b.view.Draw(screen) }