// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "time" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) // NewSpinner creates a new spinner. func NewSpinner(label string, spinner []string, app *tview.Application) *Spinner { s := &Spinner{ Box: tview.NewBox(), spinner: spinner, label: label, ticker: time.NewTicker(time.Millisecond * 50), shutdown: make(chan struct{}), stopped: make(chan struct{}), } go func() { defer func() { app.Draw() close(s.stopped) }() for { select { case <-s.shutdown: return case <-s.ticker.C: app.Draw() } } }() return s } const ( spinnerStateInProgress = iota spinnerStateComplete spinnerStateFailed ) var spinnerStatusSymbols = []string{ "", "[green::]✓[-::]", "[red::]✖[-::]", } // Spinner a unicode spinner primitive. type Spinner struct { *tview.Box spinner []string label string index int shutdown chan struct{} stopped chan struct{} ticker *time.Ticker state int } // Draw draws this primitive onto the screen. func (s *Spinner) Draw(screen tcell.Screen) { s.Box.Draw(screen) x, y, width, _ := s.GetInnerRect() var spinner string if s.state == spinnerStateInProgress { spinner = s.spinner[s.index] } else { spinner = spinnerStatusSymbols[s.state] } line := fmt.Sprintf(s.label+" %s", spinner) tview.Print(screen, line, x, y, width, tview.AlignLeft, tcell.ColorWhite) s.index++ if s.index == len(s.spinner) { s.index = 0 } } // Stop should be always called to stop the spinner background routine. func (s *Spinner) Stop(success bool) <-chan struct{} { s.state = spinnerStateInProgress if success { s.state = spinnerStateComplete } else { s.state = spinnerStateFailed } s.ticker.Stop() s.shutdown <- struct{}{} return s.stopped }