progressbarinfinite.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package widget
  2. import (
  3. "time"
  4. "fyne.io/fyne/v2"
  5. "fyne.io/fyne/v2/canvas"
  6. "fyne.io/fyne/v2/internal/cache"
  7. "fyne.io/fyne/v2/internal/widget"
  8. "fyne.io/fyne/v2/theme"
  9. )
  10. const (
  11. infiniteRefreshRate = 50 * time.Millisecond
  12. maxProgressBarInfiniteWidthRatio = 1.0 / 5
  13. minProgressBarInfiniteWidthRatio = 1.0 / 20
  14. progressBarInfiniteStepSizeRatio = 1.0 / 50
  15. )
  16. type infProgressRenderer struct {
  17. widget.BaseRenderer
  18. background, bar *canvas.Rectangle
  19. animation *fyne.Animation
  20. running bool
  21. progress *ProgressBarInfinite
  22. }
  23. // MinSize calculates the minimum size of a progress bar.
  24. func (p *infProgressRenderer) MinSize() fyne.Size {
  25. // this is to create the same size infinite progress bar as regular progress bar
  26. text := fyne.MeasureText("100%", theme.TextSize(), fyne.TextStyle{})
  27. return fyne.NewSize(text.Width+theme.InnerPadding()*2, text.Height+theme.InnerPadding())
  28. }
  29. func (p *infProgressRenderer) updateBar(done float32) {
  30. progressWidth := p.progress.size.Width
  31. spanWidth := progressWidth + (progressWidth * (maxProgressBarInfiniteWidthRatio / 2))
  32. maxBarWidth := progressWidth * maxProgressBarInfiniteWidthRatio
  33. barCenterX := spanWidth*done - (spanWidth-progressWidth)/2
  34. barPos := fyne.NewPos(barCenterX-maxBarWidth/2, 0)
  35. barSize := fyne.NewSize(maxBarWidth, p.progress.size.Height)
  36. if barPos.X < 0 {
  37. barSize.Width += barPos.X
  38. barPos.X = 0
  39. }
  40. if barPos.X+barSize.Width > progressWidth {
  41. barSize.Width = progressWidth - barPos.X
  42. }
  43. p.bar.Resize(barSize)
  44. p.bar.Move(barPos)
  45. canvas.Refresh(p.bar)
  46. }
  47. // Layout the components of the infinite progress bar
  48. func (p *infProgressRenderer) Layout(size fyne.Size) {
  49. p.background.Resize(size)
  50. }
  51. // Refresh updates the size and position of the horizontal scrolling infinite progress bar
  52. func (p *infProgressRenderer) Refresh() {
  53. if p.isRunning() {
  54. return // we refresh from the goroutine
  55. }
  56. p.background.FillColor = progressBackgroundColor()
  57. p.bar.FillColor = theme.PrimaryColor()
  58. p.background.Refresh()
  59. p.bar.Refresh()
  60. canvas.Refresh(p.progress.super())
  61. }
  62. func (p *infProgressRenderer) isRunning() bool {
  63. p.progress.propertyLock.RLock()
  64. defer p.progress.propertyLock.RUnlock()
  65. return p.running
  66. }
  67. // Start the infinite progress bar background thread to update it continuously
  68. func (p *infProgressRenderer) start() {
  69. if p.isRunning() {
  70. return
  71. }
  72. p.progress.propertyLock.Lock()
  73. defer p.progress.propertyLock.Unlock()
  74. p.animation = fyne.NewAnimation(time.Second*3, p.updateBar)
  75. p.animation.Curve = fyne.AnimationLinear
  76. p.animation.RepeatCount = fyne.AnimationRepeatForever
  77. p.running = true
  78. p.animation.Start()
  79. }
  80. // Stop the background thread from updating the infinite progress bar
  81. func (p *infProgressRenderer) stop() {
  82. p.progress.propertyLock.Lock()
  83. defer p.progress.propertyLock.Unlock()
  84. p.running = false
  85. p.animation.Stop()
  86. }
  87. func (p *infProgressRenderer) Destroy() {
  88. p.stop()
  89. }
  90. // ProgressBarInfinite widget creates a horizontal panel that indicates waiting indefinitely
  91. // An infinite progress bar loops 0% -> 100% repeatedly until Stop() is called
  92. type ProgressBarInfinite struct {
  93. BaseWidget
  94. }
  95. // Show this widget, if it was previously hidden
  96. func (p *ProgressBarInfinite) Show() {
  97. p.Start()
  98. p.BaseWidget.Show()
  99. }
  100. // Hide this widget, if it was previously visible
  101. func (p *ProgressBarInfinite) Hide() {
  102. p.Stop()
  103. p.BaseWidget.Hide()
  104. }
  105. // Start the infinite progress bar animation
  106. func (p *ProgressBarInfinite) Start() {
  107. cache.Renderer(p).(*infProgressRenderer).start()
  108. }
  109. // Stop the infinite progress bar animation
  110. func (p *ProgressBarInfinite) Stop() {
  111. cache.Renderer(p).(*infProgressRenderer).stop()
  112. }
  113. // Running returns the current state of the infinite progress animation
  114. func (p *ProgressBarInfinite) Running() bool {
  115. if !cache.IsRendered(p) {
  116. return false
  117. }
  118. return cache.Renderer(p).(*infProgressRenderer).isRunning()
  119. }
  120. // MinSize returns the size that this widget should not shrink below
  121. func (p *ProgressBarInfinite) MinSize() fyne.Size {
  122. p.ExtendBaseWidget(p)
  123. return p.BaseWidget.MinSize()
  124. }
  125. // CreateRenderer is a private method to Fyne which links this widget to its renderer
  126. func (p *ProgressBarInfinite) CreateRenderer() fyne.WidgetRenderer {
  127. p.ExtendBaseWidget(p)
  128. background := canvas.NewRectangle(progressBackgroundColor())
  129. bar := canvas.NewRectangle(theme.PrimaryColor())
  130. render := &infProgressRenderer{
  131. BaseRenderer: widget.NewBaseRenderer([]fyne.CanvasObject{background, bar}),
  132. background: background,
  133. bar: bar,
  134. progress: p,
  135. }
  136. render.start()
  137. return render
  138. }
  139. // NewProgressBarInfinite creates a new progress bar widget that loops indefinitely from 0% -> 100%
  140. // SetValue() is not defined for infinite progress bar
  141. // To stop the looping progress and set the progress bar to 100%, call ProgressBarInfinite.Stop()
  142. func NewProgressBarInfinite() *ProgressBarInfinite {
  143. p := &ProgressBarInfinite{}
  144. cache.Renderer(p).Layout(p.MinSize())
  145. return p
  146. }