refactoring: SpinnerStyleComposer
Vladimir Bauer
5 years ago
| 3 | 3 | "io" |
| 4 | 4 | "strings" |
| 5 | 5 | |
| 6 | "github.com/acarl005/stripansi" | |
| 6 | 7 | "github.com/mattn/go-runewidth" |
| 7 | 8 | "github.com/vbauerster/mpb/v6/decor" |
| 8 | 9 | "github.com/vbauerster/mpb/v6/internal" |
| 9 | 10 | ) |
| 10 | 11 | |
| 11 | // SpinnerAlignment enum. | |
| 12 | type SpinnerAlignment int | |
| 13 | ||
| 14 | // SpinnerAlignment kinds. | |
| 15 | 12 | const ( |
| 16 | SpinnerOnLeft SpinnerAlignment = iota | |
| 17 | SpinnerOnMiddle | |
| 18 | SpinnerOnRight | |
| 13 | positionLeft = 1 + iota | |
| 14 | positionRight | |
| 19 | 15 | ) |
| 20 | 16 | |
| 21 | // SpinnerDefaultStyle is a style for rendering a spinner. | |
| 22 | var SpinnerDefaultStyle = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | |
| 23 | ||
| 24 | type spinnerFiller struct { | |
| 25 | frames []string | |
| 26 | count uint | |
| 27 | alignment SpinnerAlignment | |
| 17 | // SpinnerStyleComposer interface. | |
| 18 | type SpinnerStyleComposer interface { | |
| 19 | BarFillerBuilder | |
| 20 | PositionLeft() SpinnerStyleComposer | |
| 21 | PositionRight() SpinnerStyleComposer | |
| 28 | 22 | } |
| 29 | 23 | |
| 30 | // NewSpinnerFiller returns a BarFiller implementation which renders | |
| 31 | // a spinner. If style is nil or zero length, SpinnerDefaultStyle is | |
| 32 | // applied. To be used with `*Progress.Add(...) *Bar` method. | |
| 33 | func NewSpinnerFiller(style []string, alignment SpinnerAlignment) BarFiller { | |
| 34 | if len(style) == 0 { | |
| 35 | style = SpinnerDefaultStyle | |
| 36 | } | |
| 37 | filler := &spinnerFiller{ | |
| 38 | frames: style, | |
| 39 | alignment: alignment, | |
| 40 | } | |
| 41 | return filler | |
| 24 | type sFiller struct { | |
| 25 | count uint | |
| 26 | position uint | |
| 27 | frames []string | |
| 42 | 28 | } |
| 43 | 29 | |
| 44 | func (s *spinnerFiller) Fill(w io.Writer, reqWidth int, stat decor.Statistics) { | |
| 45 | width := internal.CheckRequestedWidth(reqWidth, stat.AvailableWidth) | |
| 30 | type spinnerStyle struct { | |
| 31 | position uint | |
| 32 | frames []string | |
| 33 | } | |
| 34 | ||
| 35 | // SpinnerStyle constructs default spinner style which can be altered via | |
| 36 | // SpinnerStyleComposer interface. | |
| 37 | func SpinnerStyle(frames ...string) SpinnerStyleComposer { | |
| 38 | ss := new(spinnerStyle) | |
| 39 | if len(frames) != 0 { | |
| 40 | ss.frames = append(ss.frames, frames...) | |
| 41 | } else { | |
| 42 | ss.frames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} | |
| 43 | } | |
| 44 | return ss | |
| 45 | } | |
| 46 | ||
| 47 | func (s *spinnerStyle) PositionLeft() SpinnerStyleComposer { | |
| 48 | s.position = positionLeft | |
| 49 | return s | |
| 50 | } | |
| 51 | ||
| 52 | func (s *spinnerStyle) PositionRight() SpinnerStyleComposer { | |
| 53 | s.position = positionRight | |
| 54 | return s | |
| 55 | } | |
| 56 | ||
| 57 | func (s *spinnerStyle) Build() BarFiller { | |
| 58 | sf := &sFiller{ | |
| 59 | position: s.position, | |
| 60 | frames: s.frames, | |
| 61 | } | |
| 62 | return sf | |
| 63 | } | |
| 64 | ||
| 65 | func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) { | |
| 66 | width = internal.CheckRequestedWidth(width, stat.AvailableWidth) | |
| 46 | 67 | |
| 47 | 68 | frame := s.frames[s.count%uint(len(s.frames))] |
| 48 | frameWidth := runewidth.StringWidth(frame) | |
| 69 | frameWidth := runewidth.StringWidth(stripansi.Strip(frame)) | |
| 49 | 70 | |
| 50 | 71 | if width < frameWidth { |
| 51 | 72 | return |
| 52 | 73 | } |
| 53 | 74 | |
| 54 | switch rest := width - frameWidth; s.alignment { | |
| 55 | case SpinnerOnLeft: | |
| 75 | rest := width - frameWidth | |
| 76 | switch s.position { | |
| 77 | case positionLeft: | |
| 56 | 78 | io.WriteString(w, frame+strings.Repeat(" ", rest)) |
| 57 | case SpinnerOnMiddle: | |
| 79 | case positionRight: | |
| 80 | io.WriteString(w, strings.Repeat(" ", rest)+frame) | |
| 81 | default: | |
| 58 | 82 | str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2) |
| 59 | 83 | io.WriteString(w, str) |
| 60 | case SpinnerOnRight: | |
| 61 | io.WriteString(w, strings.Repeat(" ", rest)+frame) | |
| 62 | 84 | } |
| 63 | 85 | s.count++ |
| 64 | 86 | } |