Skip to content

Commit

Permalink
added stop flag to object, added new fields to animation runner to ha…
Browse files Browse the repository at this point in the history
…ndle animation stop immediately after start and animation stop inside tick function, added test
  • Loading branch information
fpabl0 authored and andydotxyz committed Feb 26, 2021
1 parent b24b04f commit d983d57
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 12 deletions.
18 changes: 18 additions & 0 deletions internal/animation/animation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package animation

import (
"sync"
"time"

"fyne.io/fyne/v2"
Expand All @@ -13,11 +14,28 @@ type anim struct {
reverse bool
start time.Time
total int64

mu sync.RWMutex
stopped bool
}

func newAnim(a *fyne.Animation) *anim {
// TODO should we add a nil check here? (to avoid panic)
animate := &anim{a: a, start: time.Now(), end: time.Now().Add(a.Duration)}
animate.total = animate.end.Sub(animate.start).Nanoseconds() / 1000000 // TODO change this to Milliseconds() when we drop Go 1.12
animate.repeatsLeft = a.RepeatCount
return animate
}

func (a *anim) setStopFlag() {
a.mu.Lock()
a.stopped = true
a.mu.Unlock()
}

func (a *anim) isStopped() bool {
a.mu.RLock()
ret := a.stopped
a.mu.RUnlock()
return ret
}
42 changes: 42 additions & 0 deletions internal/animation/animation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package animation

import (
"sync"
"testing"
"time"

Expand Down Expand Up @@ -48,3 +49,44 @@ func TestGLDriver_StopAnimation(t *testing.T) {
run.Stop(a)
assert.Zero(t, len(run.animations))
}

func TestGLDriver_StopAnimationImmediatelyAndInsideTick(t *testing.T) {
var wg sync.WaitGroup
run := &Runner{}

// stopping an animation immediately after start, should be effectively removed
// from the internal animation list (first one is added directly to animation list)
a := &fyne.Animation{
Duration: time.Second,
Tick: func(f float32) {},
}
run.Start(a)
run.Stop(a)

// stopping animation inside tick function
for i := 0; i < 10; i++ {
wg.Add(1)
var b *fyne.Animation
b = &fyne.Animation{
Duration: time.Second,
Tick: func(d float32) {
run.Stop(b)
wg.Done()
}}
run.Start(b)
}

// Similar to first part, but in this time this animation should be added and then removed
// from pendingAnimation slice.
c := &fyne.Animation{
Duration: time.Second,
Tick: func(f float32) {},
}
run.Start(c)
run.Stop(c)

wg.Wait()
run.animationMutex.RLock()
assert.Zero(t, len(run.animations))
run.animationMutex.RUnlock()
}
49 changes: 37 additions & 12 deletions internal/animation/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,56 @@ import (

// Runner is the main driver for animations package
type Runner struct {
animationMutex sync.RWMutex
animations []*anim
animationMutex sync.RWMutex
animations []*anim
pendingAnimations []*anim

runnerStarted bool
}

// Start will register the passed application and initiate its ticking.
func (r *Runner) Start(a *fyne.Animation) {
r.animationMutex.Lock()
defer r.animationMutex.Unlock()
wasStopped := len(r.animations) == 0

r.animations = append(r.animations, newAnim(a))
if wasStopped {
if !r.runnerStarted {
r.runnerStarted = true
r.animations = append(r.animations, newAnim(a))
r.runAnimations()
} else {
r.pendingAnimations = append(r.pendingAnimations, newAnim(a))
}
}

// Stop causes an animation to stop ticking (if it was still running) and removes it from the runner.
func (r *Runner) Stop(a *fyne.Animation) {
r.animationMutex.Lock()
defer r.animationMutex.Unlock()
oldList := r.animations
var newList []*anim
for _, item := range oldList {

newList := make([]*anim, 0, len(r.animations))
stopped := false
for _, item := range r.animations {
if item.a != a {
newList = append(newList, item)
} else {
item.setStopFlag()
stopped = true
}
}
r.animations = newList
if stopped {
return
}

newList = make([]*anim, 0, len(r.pendingAnimations))
for _, item := range r.pendingAnimations {
if item.a != a {
newList = append(newList, item)
} else {
item.setStopFlag()
}
}
r.pendingAnimations = newList
}

func (r *Runner) runAnimations() {
Expand All @@ -47,19 +69,22 @@ func (r *Runner) runAnimations() {
<-draw.C
r.animationMutex.Lock()
oldList := r.animations
r.animations = nil // clear the list so we can append any new ones after processing
r.animationMutex.Unlock()
var newList []*anim
newList := make([]*anim, 0, len(oldList))
for _, a := range oldList {
if r.tickAnimation(a) {
if r.tickAnimation(a) && !a.isStopped() {
newList = append(newList, a)
}
}
r.animationMutex.Lock()
r.animations = append(newList, r.animations...)
r.animations = append(newList, r.pendingAnimations...)
r.pendingAnimations = nil
done = len(r.animations) == 0
r.animationMutex.Unlock()
}
r.animationMutex.Lock()
r.runnerStarted = false
r.animationMutex.Unlock()
draw.Stop()
}()
}
Expand Down

0 comments on commit d983d57

Please sign in to comment.