| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/compositor/layer_animation_sequence.h" |
| |
| #include <algorithm> |
| #include <iterator> |
| |
| #include "base/trace_event/trace_event.h" |
| #include "cc/animation/animation_id_provider.h" |
| #include "ui/compositor/layer_animation_delegate.h" |
| #include "ui/compositor/layer_animation_element.h" |
| #include "ui/compositor/layer_animation_observer.h" |
| |
| namespace ui { |
| |
| LayerAnimationSequence::LayerAnimationSequence() |
| : properties_(LayerAnimationElement::UNKNOWN), |
| is_cyclic_(false), |
| last_element_(0), |
| waiting_for_group_start_(false), |
| animation_group_id_(0), |
| last_progressed_fraction_(0.0), |
| weak_ptr_factory_(this) { |
| } |
| |
| LayerAnimationSequence::LayerAnimationSequence(LayerAnimationElement* element) |
| : properties_(LayerAnimationElement::UNKNOWN), |
| is_cyclic_(false), |
| last_element_(0), |
| waiting_for_group_start_(false), |
| animation_group_id_(0), |
| last_progressed_fraction_(0.0), |
| weak_ptr_factory_(this) { |
| AddElement(element); |
| } |
| |
| LayerAnimationSequence::~LayerAnimationSequence() { |
| FOR_EACH_OBSERVER(LayerAnimationObserver, |
| observers_, |
| DetachedFromSequence(this, true)); |
| } |
| |
| void LayerAnimationSequence::Start(LayerAnimationDelegate* delegate) { |
| DCHECK(start_time_ != base::TimeTicks()); |
| last_progressed_fraction_ = 0.0; |
| if (elements_.empty()) |
| return; |
| |
| elements_[0]->set_requested_start_time(start_time_); |
| elements_[0]->Start(delegate, animation_group_id_); |
| } |
| |
| void LayerAnimationSequence::Progress(base::TimeTicks now, |
| LayerAnimationDelegate* delegate) { |
| DCHECK(start_time_ != base::TimeTicks()); |
| bool redraw_required = false; |
| |
| if (elements_.empty()) |
| return; |
| |
| if (last_element_ == 0) |
| last_start_ = start_time_; |
| |
| size_t current_index = last_element_ % elements_.size(); |
| base::TimeDelta element_duration; |
| while (is_cyclic_ || last_element_ < elements_.size()) { |
| elements_[current_index]->set_requested_start_time(last_start_); |
| if (!elements_[current_index]->IsFinished(now, &element_duration)) |
| break; |
| |
| // Let the element we're passing finish. |
| if (elements_[current_index]->ProgressToEnd(delegate)) |
| redraw_required = true; |
| last_start_ += element_duration; |
| ++last_element_; |
| last_progressed_fraction_ = |
| elements_[current_index]->last_progressed_fraction(); |
| current_index = last_element_ % elements_.size(); |
| } |
| |
| if (is_cyclic_ || last_element_ < elements_.size()) { |
| if (!elements_[current_index]->Started()) { |
| animation_group_id_ = cc::AnimationIdProvider::NextGroupId(); |
| elements_[current_index]->Start(delegate, animation_group_id_); |
| } |
| base::WeakPtr<LayerAnimationSequence> alive(weak_ptr_factory_.GetWeakPtr()); |
| if (elements_[current_index]->Progress(now, delegate)) |
| redraw_required = true; |
| if (!alive) |
| return; |
| last_progressed_fraction_ = |
| elements_[current_index]->last_progressed_fraction(); |
| } |
| |
| // Since the delegate may be deleted due to the notifications below, it is |
| // important that we schedule a draw before sending them. |
| if (redraw_required) |
| delegate->ScheduleDrawForAnimation(); |
| |
| if (!is_cyclic_ && last_element_ == elements_.size()) { |
| last_element_ = 0; |
| waiting_for_group_start_ = false; |
| animation_group_id_ = 0; |
| NotifyEnded(); |
| } |
| } |
| |
| bool LayerAnimationSequence::IsFinished(base::TimeTicks time) { |
| if (is_cyclic_ || waiting_for_group_start_) |
| return false; |
| |
| if (elements_.empty()) |
| return true; |
| |
| if (last_element_ == 0) |
| last_start_ = start_time_; |
| |
| base::TimeTicks current_start = last_start_; |
| size_t current_index = last_element_; |
| base::TimeDelta element_duration; |
| while (current_index < elements_.size()) { |
| elements_[current_index]->set_requested_start_time(current_start); |
| if (!elements_[current_index]->IsFinished(time, &element_duration)) |
| break; |
| |
| current_start += element_duration; |
| ++current_index; |
| } |
| |
| return (current_index == elements_.size()); |
| } |
| |
| void LayerAnimationSequence::ProgressToEnd(LayerAnimationDelegate* delegate) { |
| bool redraw_required = false; |
| |
| if (elements_.empty()) |
| return; |
| |
| size_t current_index = last_element_ % elements_.size(); |
| while (current_index < elements_.size()) { |
| if (elements_[current_index]->ProgressToEnd(delegate)) |
| redraw_required = true; |
| last_progressed_fraction_ = |
| elements_[current_index]->last_progressed_fraction(); |
| ++current_index; |
| ++last_element_; |
| } |
| |
| if (redraw_required) |
| delegate->ScheduleDrawForAnimation(); |
| |
| if (!is_cyclic_) { |
| last_element_ = 0; |
| waiting_for_group_start_ = false; |
| animation_group_id_ = 0; |
| NotifyEnded(); |
| } |
| } |
| |
| void LayerAnimationSequence::GetTargetValue( |
| LayerAnimationElement::TargetValue* target) const { |
| if (is_cyclic_) |
| return; |
| |
| for (size_t i = last_element_; i < elements_.size(); ++i) |
| elements_[i]->GetTargetValue(target); |
| } |
| |
| void LayerAnimationSequence::Abort(LayerAnimationDelegate* delegate) { |
| size_t current_index = last_element_ % elements_.size(); |
| while (current_index < elements_.size()) { |
| elements_[current_index]->Abort(delegate); |
| ++current_index; |
| } |
| last_element_ = 0; |
| waiting_for_group_start_ = false; |
| NotifyAborted(); |
| } |
| |
| void LayerAnimationSequence::AddElement(LayerAnimationElement* element) { |
| properties_ |= element->properties(); |
| elements_.push_back(make_linked_ptr(element)); |
| } |
| |
| bool LayerAnimationSequence::HasConflictingProperty( |
| LayerAnimationElement::AnimatableProperties other) const { |
| return (properties_ & other) != LayerAnimationElement::UNKNOWN; |
| } |
| |
| bool LayerAnimationSequence::IsFirstElementThreaded() const { |
| if (!elements_.empty()) |
| return elements_[0]->IsThreaded(); |
| |
| return false; |
| } |
| |
| void LayerAnimationSequence::AddObserver(LayerAnimationObserver* observer) { |
| if (!observers_.HasObserver(observer)) { |
| observers_.AddObserver(observer); |
| observer->AttachedToSequence(this); |
| } |
| } |
| |
| void LayerAnimationSequence::RemoveObserver(LayerAnimationObserver* observer) { |
| observers_.RemoveObserver(observer); |
| observer->DetachedFromSequence(this, true); |
| } |
| |
| void LayerAnimationSequence::OnThreadedAnimationStarted( |
| const cc::AnimationEvent& event) { |
| if (elements_.empty() || event.group_id != animation_group_id_) |
| return; |
| |
| size_t current_index = last_element_ % elements_.size(); |
| LayerAnimationElement::AnimatableProperties element_properties = |
| elements_[current_index]->properties(); |
| LayerAnimationElement::AnimatableProperty event_property = |
| LayerAnimationElement::ToAnimatableProperty(event.target_property); |
| DCHECK(element_properties & event_property); |
| elements_[current_index]->set_effective_start_time(event.monotonic_time); |
| } |
| |
| void LayerAnimationSequence::OnScheduled() { |
| NotifyScheduled(); |
| } |
| |
| void LayerAnimationSequence::OnAnimatorDestroyed() { |
| if (observers_.might_have_observers()) { |
| ObserverListBase<LayerAnimationObserver>::Iterator it(observers_); |
| LayerAnimationObserver* obs; |
| while ((obs = it.GetNext()) != NULL) { |
| if (!obs->RequiresNotificationWhenAnimatorDestroyed()) { |
| // Remove the observer, but do not allow notifications to be sent. |
| observers_.RemoveObserver(obs); |
| obs->DetachedFromSequence(this, false); |
| } |
| } |
| } |
| } |
| |
| size_t LayerAnimationSequence::size() const { |
| return elements_.size(); |
| } |
| |
| LayerAnimationElement* LayerAnimationSequence::FirstElement() const { |
| if (elements_.empty()) { |
| return NULL; |
| } |
| |
| return elements_[0].get(); |
| } |
| |
| void LayerAnimationSequence::NotifyScheduled() { |
| FOR_EACH_OBSERVER(LayerAnimationObserver, |
| observers_, |
| OnLayerAnimationScheduled(this)); |
| } |
| |
| void LayerAnimationSequence::NotifyEnded() { |
| FOR_EACH_OBSERVER(LayerAnimationObserver, |
| observers_, |
| OnLayerAnimationEnded(this)); |
| } |
| |
| void LayerAnimationSequence::NotifyAborted() { |
| FOR_EACH_OBSERVER(LayerAnimationObserver, |
| observers_, |
| OnLayerAnimationAborted(this)); |
| } |
| |
| LayerAnimationElement* LayerAnimationSequence::CurrentElement() const { |
| if (elements_.empty()) |
| return NULL; |
| |
| size_t current_index = last_element_ % elements_.size(); |
| return elements_[current_index].get(); |
| } |
| |
| } // namespace ui |