| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- package widget
- import (
- "time"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/canvas"
- "fyne.io/fyne/v2/internal/cache"
- "fyne.io/fyne/v2/internal/widget"
- "fyne.io/fyne/v2/theme"
- )
- const (
- infiniteRefreshRate = 50 * time.Millisecond
- maxProgressBarInfiniteWidthRatio = 1.0 / 5
- minProgressBarInfiniteWidthRatio = 1.0 / 20
- progressBarInfiniteStepSizeRatio = 1.0 / 50
- )
- type infProgressRenderer struct {
- widget.BaseRenderer
- background, bar *canvas.Rectangle
- animation *fyne.Animation
- running bool
- progress *ProgressBarInfinite
- }
- // MinSize calculates the minimum size of a progress bar.
- func (p *infProgressRenderer) MinSize() fyne.Size {
- // this is to create the same size infinite progress bar as regular progress bar
- text := fyne.MeasureText("100%", theme.TextSize(), fyne.TextStyle{})
- return fyne.NewSize(text.Width+theme.InnerPadding()*2, text.Height+theme.InnerPadding()*2)
- }
- func (p *infProgressRenderer) updateBar(done float32) {
- progressWidth := p.progress.size.Width
- spanWidth := progressWidth + (progressWidth * (maxProgressBarInfiniteWidthRatio / 2))
- maxBarWidth := progressWidth * maxProgressBarInfiniteWidthRatio
- barCenterX := spanWidth*done - (spanWidth-progressWidth)/2
- barPos := fyne.NewPos(barCenterX-maxBarWidth/2, 0)
- barSize := fyne.NewSize(maxBarWidth, p.progress.size.Height)
- if barPos.X < 0 {
- barSize.Width += barPos.X
- barPos.X = 0
- }
- if barPos.X+barSize.Width > progressWidth {
- barSize.Width = progressWidth - barPos.X
- }
- p.bar.Resize(barSize)
- p.bar.Move(barPos)
- canvas.Refresh(p.bar)
- }
- // Layout the components of the infinite progress bar
- func (p *infProgressRenderer) Layout(size fyne.Size) {
- p.background.Resize(size)
- }
- // Refresh updates the size and position of the horizontal scrolling infinite progress bar
- func (p *infProgressRenderer) Refresh() {
- if p.isRunning() {
- return // we refresh from the goroutine
- }
- p.background.FillColor = progressBackgroundColor()
- p.background.CornerRadius = theme.InputRadiusSize()
- p.bar.FillColor = theme.PrimaryColor()
- p.bar.CornerRadius = theme.InputRadiusSize()
- p.background.Refresh()
- p.bar.Refresh()
- canvas.Refresh(p.progress.super())
- }
- func (p *infProgressRenderer) isRunning() bool {
- p.progress.propertyLock.RLock()
- defer p.progress.propertyLock.RUnlock()
- return p.running
- }
- // Start the infinite progress bar background thread to update it continuously
- func (p *infProgressRenderer) start() {
- if p.isRunning() {
- return
- }
- p.progress.propertyLock.Lock()
- defer p.progress.propertyLock.Unlock()
- p.animation = fyne.NewAnimation(time.Second*3, p.updateBar)
- p.animation.Curve = fyne.AnimationLinear
- p.animation.RepeatCount = fyne.AnimationRepeatForever
- p.running = true
- p.animation.Start()
- }
- // Stop the background thread from updating the infinite progress bar
- func (p *infProgressRenderer) stop() {
- p.progress.propertyLock.Lock()
- defer p.progress.propertyLock.Unlock()
- p.running = false
- p.animation.Stop()
- }
- func (p *infProgressRenderer) Destroy() {
- p.stop()
- }
- // ProgressBarInfinite widget creates a horizontal panel that indicates waiting indefinitely
- // An infinite progress bar loops 0% -> 100% repeatedly until Stop() is called
- type ProgressBarInfinite struct {
- BaseWidget
- }
- // Show this widget, if it was previously hidden
- func (p *ProgressBarInfinite) Show() {
- p.Start()
- p.BaseWidget.Show()
- }
- // Hide this widget, if it was previously visible
- func (p *ProgressBarInfinite) Hide() {
- p.Stop()
- p.BaseWidget.Hide()
- }
- // Start the infinite progress bar animation
- func (p *ProgressBarInfinite) Start() {
- cache.Renderer(p).(*infProgressRenderer).start()
- }
- // Stop the infinite progress bar animation
- func (p *ProgressBarInfinite) Stop() {
- cache.Renderer(p).(*infProgressRenderer).stop()
- }
- // Running returns the current state of the infinite progress animation
- func (p *ProgressBarInfinite) Running() bool {
- if !cache.IsRendered(p) {
- return false
- }
- return cache.Renderer(p).(*infProgressRenderer).isRunning()
- }
- // MinSize returns the size that this widget should not shrink below
- func (p *ProgressBarInfinite) MinSize() fyne.Size {
- p.ExtendBaseWidget(p)
- return p.BaseWidget.MinSize()
- }
- // CreateRenderer is a private method to Fyne which links this widget to its renderer
- func (p *ProgressBarInfinite) CreateRenderer() fyne.WidgetRenderer {
- p.ExtendBaseWidget(p)
- background := canvas.NewRectangle(progressBackgroundColor())
- background.CornerRadius = theme.InputRadiusSize()
- bar := canvas.NewRectangle(theme.PrimaryColor())
- bar.CornerRadius = theme.InputRadiusSize()
- render := &infProgressRenderer{
- BaseRenderer: widget.NewBaseRenderer([]fyne.CanvasObject{background, bar}),
- background: background,
- bar: bar,
- progress: p,
- }
- render.start()
- return render
- }
- // NewProgressBarInfinite creates a new progress bar widget that loops indefinitely from 0% -> 100%
- // SetValue() is not defined for infinite progress bar
- // To stop the looping progress and set the progress bar to 100%, call ProgressBarInfinite.Stop()
- func NewProgressBarInfinite() *ProgressBarInfinite {
- p := &ProgressBarInfinite{}
- cache.Renderer(p).Layout(p.MinSize())
- return p
- }
|