1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/gfx/animation/animation_container.h" 6 7 #include "ui/gfx/animation/animation_container_element.h" 8 #include "ui/gfx/animation/animation_container_observer.h" 9 #include "ui/gfx/frame_time.h" 10 11 using base::TimeDelta; 12 using base::TimeTicks; 13 14 namespace gfx { 15 16 AnimationContainer::AnimationContainer() 17 : last_tick_time_(gfx::FrameTime::Now()), 18 observer_(NULL) { 19 } 20 21 AnimationContainer::~AnimationContainer() { 22 // The animations own us and stop themselves before being deleted. If 23 // elements_ is not empty, something is wrong. 24 DCHECK(elements_.empty()); 25 } 26 27 void AnimationContainer::Start(AnimationContainerElement* element) { 28 DCHECK(elements_.count(element) == 0); // Start should only be invoked if the 29 // element isn't running. 30 31 if (elements_.empty()) { 32 last_tick_time_ = gfx::FrameTime::Now(); 33 SetMinTimerInterval(element->GetTimerInterval()); 34 } else if (element->GetTimerInterval() < min_timer_interval_) { 35 SetMinTimerInterval(element->GetTimerInterval()); 36 } 37 38 element->SetStartTime(last_tick_time_); 39 elements_.insert(element); 40 } 41 42 void AnimationContainer::Stop(AnimationContainerElement* element) { 43 DCHECK(elements_.count(element) > 0); // The element must be running. 44 45 elements_.erase(element); 46 47 if (elements_.empty()) { 48 timer_.Stop(); 49 if (observer_) 50 observer_->AnimationContainerEmpty(this); 51 } else { 52 TimeDelta min_timer_interval = GetMinInterval(); 53 if (min_timer_interval > min_timer_interval_) 54 SetMinTimerInterval(min_timer_interval); 55 } 56 } 57 58 void AnimationContainer::Run() { 59 // We notify the observer after updating all the elements. If all the elements 60 // are deleted as a result of updating then our ref count would go to zero and 61 // we would be deleted before we notify our observer. We add a reference to 62 // ourself here to make sure we're still valid after running all the elements. 63 scoped_refptr<AnimationContainer> this_ref(this); 64 65 TimeTicks current_time = gfx::FrameTime::Now(); 66 67 last_tick_time_ = current_time; 68 69 // Make a copy of the elements to iterate over so that if any elements are 70 // removed as part of invoking Step there aren't any problems. 71 Elements elements = elements_; 72 73 for (Elements::const_iterator i = elements.begin(); 74 i != elements.end(); ++i) { 75 // Make sure the element is still valid. 76 if (elements_.find(*i) != elements_.end()) 77 (*i)->Step(current_time); 78 } 79 80 if (observer_) 81 observer_->AnimationContainerProgressed(this); 82 } 83 84 void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) { 85 // This doesn't take into account how far along the current element is, but 86 // that shouldn't be a problem for uses of Animation/AnimationContainer. 87 timer_.Stop(); 88 min_timer_interval_ = delta; 89 timer_.Start(FROM_HERE, min_timer_interval_, this, &AnimationContainer::Run); 90 } 91 92 TimeDelta AnimationContainer::GetMinInterval() { 93 DCHECK(!elements_.empty()); 94 95 TimeDelta min; 96 Elements::const_iterator i = elements_.begin(); 97 min = (*i)->GetTimerInterval(); 98 for (++i; i != elements_.end(); ++i) { 99 if ((*i)->GetTimerInterval() < min) 100 min = (*i)->GetTimerInterval(); 101 } 102 return min; 103 } 104 105 } // namespace gfx 106