Commit 5b3d5a41 authored by sangwoo.ko's avatar sangwoo.ko Committed by Commit Bot

Introduce compositor-based AnimationRunner.

Previously, animations were ticked by base::Timer. This wasn't aligned
with Compositor so animation can jank or stutter. Thus, we need a way
to use custom animation runner which is aligned to compositor's frame.

In favor of layering, we should inject custom animation runner from
a layer views/ or chrome/ to animation system. For this reason,
AnimationRunner is introduced and clients can inject this dependcency
via AnimationRunner. For example, Widget provides
CompositorAnimationRunner which observes compositors animation frame.

TODO: We need to investigate other callsites where
gfx::Animation/LinearAnimations are used.

Bug: 824026
Change-Id: Id4d7d88a38a823fea7c860510f11ba38f7eb4e11
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1475023
Commit-Queue: Sang Woo Ko <sangwoo108@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Reviewed-by: default avatarAlan Cutter <alancutter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657587}
parent 5149d031
...@@ -82,41 +82,6 @@ class TestAXEventObserver : public views::AXEventObserver { ...@@ -82,41 +82,6 @@ class TestAXEventObserver : public views::AXEventObserver {
DISALLOW_COPY_AND_ASSIGN(TestAXEventObserver); DISALLOW_COPY_AND_ASSIGN(TestAXEventObserver);
}; };
class AnimationWaiter {
public:
AnimationWaiter(TabStrip* tab_strip, base::TimeDelta duration)
: tab_strip_(tab_strip), duration_(duration) {}
~AnimationWaiter() = default;
// Blocks until |tab_strip_| is not animating.
void Wait() {
interval_timer_.Start(
FROM_HERE, duration_,
base::BindRepeating(&AnimationWaiter::CheckAnimationEnds,
base::Unretained(this)));
run_loop_.Run();
}
private:
void CheckAnimationEnds() {
if (tab_strip_->IsAnimating())
return;
interval_timer_.Stop();
run_loop_.Quit();
}
TabStrip* tab_strip_;
base::RepeatingTimer interval_timer_;
base::TimeDelta duration_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(AnimationWaiter);
};
} // namespace } // namespace
class TestTabStripObserver : public TabStripObserver { class TestTabStripObserver : public TabStripObserver {
...@@ -867,6 +832,8 @@ TEST_P(TabStripTest, ResetBoundsForDraggedTabs) { ...@@ -867,6 +832,8 @@ TEST_P(TabStripTest, ResetBoundsForDraggedTabs) {
Tab* dragged_tab = tab_strip_->tab_at(dragged_tab_index); Tab* dragged_tab = tab_strip_->tab_at(dragged_tab_index);
dragged_tab->set_dragging(true); dragged_tab->set_dragging(true);
gfx::AnimationContainerTestApi test_api(bounds_animator()->container());
// Ending the drag triggers the tabstrip to begin animating this tab back // Ending the drag triggers the tabstrip to begin animating this tab back
// to its ideal bounds. // to its ideal bounds.
StopDraggingTab(dragged_tab); StopDraggingTab(dragged_tab);
...@@ -881,8 +848,7 @@ TEST_P(TabStripTest, ResetBoundsForDraggedTabs) { ...@@ -881,8 +848,7 @@ TEST_P(TabStripTest, ResetBoundsForDraggedTabs) {
// than the original ones (where it's an active tab). // than the original ones (where it's an active tab).
const auto duration = base::TimeDelta::FromMilliseconds( const auto duration = base::TimeDelta::FromMilliseconds(
bounds_animator()->GetAnimationDuration()); bounds_animator()->GetAnimationDuration());
AnimationWaiter waiter(tab_strip_, duration); test_api.IncrementTime(duration);
waiter.Wait();
EXPECT_FALSE(dragged_tab->dragging()); EXPECT_FALSE(dragged_tab->dragging());
EXPECT_LT(dragged_tab->bounds().width(), min_active_width); EXPECT_LT(dragged_tab->bounds().width(), min_active_width);
......
...@@ -22,6 +22,8 @@ jumbo_component("animation") { ...@@ -22,6 +22,8 @@ jumbo_component("animation") {
"animation_delegate.h", "animation_delegate.h",
"animation_export.h", "animation_export.h",
"animation_mac.mm", "animation_mac.mm",
"animation_runner.cc",
"animation_runner.h",
"animation_win.cc", "animation_win.cc",
"linear_animation.cc", "linear_animation.cc",
"linear_animation.h", "linear_animation.h",
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "ui/gfx/animation/animation.h" #include "ui/gfx/animation/animation.h"
#include <memory>
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "ui/gfx/animation/animation_container.h" #include "ui/gfx/animation/animation_container.h"
...@@ -37,8 +39,11 @@ void Animation::Start() { ...@@ -37,8 +39,11 @@ void Animation::Start() {
if (is_animating_) if (is_animating_)
return; return;
if (!container_.get()) if (!container_) {
container_ = new AnimationContainer(); container_ = base::MakeRefCounted<AnimationContainer>();
if (delegate_)
delegate_->AnimationContainerWasSet(container_.get());
}
is_animating_ = true; is_animating_ = true;
...@@ -92,6 +97,9 @@ void Animation::SetContainer(AnimationContainer* container) { ...@@ -92,6 +97,9 @@ void Animation::SetContainer(AnimationContainer* container) {
else else
container_ = new AnimationContainer(); container_ = new AnimationContainer();
if (delegate_)
delegate_->AnimationContainerWasSet(container_.get());
if (is_animating_) if (is_animating_)
container_->Start(this); container_->Start(this);
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "ui/gfx/animation/animation_container.h" #include "ui/gfx/animation/animation_container.h"
#include "base/bind.h"
#include "ui/gfx/animation/animation_container_element.h" #include "ui/gfx/animation/animation_container_element.h"
#include "ui/gfx/animation/animation_container_observer.h" #include "ui/gfx/animation/animation_container_observer.h"
...@@ -12,10 +13,7 @@ using base::TimeTicks; ...@@ -12,10 +13,7 @@ using base::TimeTicks;
namespace gfx { namespace gfx {
AnimationContainer::AnimationContainer() AnimationContainer::AnimationContainer() = default;
: last_tick_time_(base::TimeTicks::Now()),
min_timer_interval_count_(0),
observer_(NULL) {}
AnimationContainer::~AnimationContainer() { AnimationContainer::~AnimationContainer() {
// The animations own us and stop themselves before being deleted. If // The animations own us and stop themselves before being deleted. If
...@@ -49,7 +47,7 @@ void AnimationContainer::Stop(AnimationContainerElement* element) { ...@@ -49,7 +47,7 @@ void AnimationContainer::Stop(AnimationContainerElement* element) {
elements_.erase(element); elements_.erase(element);
if (elements_.empty()) { if (elements_.empty()) {
timer_.Stop(); runner_->Stop();
min_timer_interval_count_ = 0; min_timer_interval_count_ = 0;
if (observer_) if (observer_)
observer_->AnimationContainerEmpty(this); observer_->AnimationContainerEmpty(this);
...@@ -69,15 +67,21 @@ void AnimationContainer::Stop(AnimationContainerElement* element) { ...@@ -69,15 +67,21 @@ void AnimationContainer::Stop(AnimationContainerElement* element) {
} }
} }
void AnimationContainer::Run() { void AnimationContainer::SetAnimationRunner(
std::unique_ptr<AnimationRunner> runner) {
has_custom_animation_runner_ = !!runner;
runner_ = has_custom_animation_runner_
? std::move(runner)
: AnimationRunner::CreateDefaultAnimationRunner();
}
void AnimationContainer::Run(base::TimeTicks current_time) {
// We notify the observer after updating all the elements. If all the elements // We notify the observer after updating all the elements. If all the elements
// are deleted as a result of updating then our ref count would go to zero and // are deleted as a result of updating then our ref count would go to zero and
// we would be deleted before we notify our observer. We add a reference to // we would be deleted before we notify our observer. We add a reference to
// ourself here to make sure we're still valid after running all the elements. // ourself here to make sure we're still valid after running all the elements.
scoped_refptr<AnimationContainer> this_ref(this); scoped_refptr<AnimationContainer> this_ref(this);
TimeTicks current_time = base::TimeTicks::Now();
last_tick_time_ = current_time; last_tick_time_ = current_time;
// Make a copy of the elements to iterate over so that if any elements are // Make a copy of the elements to iterate over so that if any elements are
...@@ -98,9 +102,11 @@ void AnimationContainer::Run() { ...@@ -98,9 +102,11 @@ void AnimationContainer::Run() {
void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) { void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) {
// This doesn't take into account how far along the current element is, but // This doesn't take into account how far along the current element is, but
// that shouldn't be a problem for uses of Animation/AnimationContainer. // that shouldn't be a problem for uses of Animation/AnimationContainer.
timer_.Stop(); runner_->Stop();
min_timer_interval_ = delta; min_timer_interval_ = delta;
timer_.Start(FROM_HERE, min_timer_interval_, this, &AnimationContainer::Run); runner_->Start(
min_timer_interval_,
base::BindRepeating(&AnimationContainer::Run, base::Unretained(this)));
} }
std::pair<TimeDelta, size_t> AnimationContainer::GetMinIntervalAndCount() std::pair<TimeDelta, size_t> AnimationContainer::GetMinIntervalAndCount()
......
...@@ -5,25 +5,27 @@ ...@@ -5,25 +5,27 @@
#ifndef UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_ #ifndef UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_
#define UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_ #define UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_
#include <memory>
#include <utility> #include <utility>
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h"
#include "ui/gfx/animation/animation_export.h" #include "ui/gfx/animation/animation_export.h"
#include "ui/gfx/animation/animation_runner.h"
namespace gfx { namespace gfx {
class AnimationContainerElement; class AnimationContainerElement;
class AnimationContainerObserver; class AnimationContainerObserver;
// AnimationContainer is used by Animation to manage the underlying timer. // AnimationContainer is used by Animation to manage the underlying
// Internally each Animation creates a single AnimationContainer. You can // AnimationRunner. Internally each Animation creates a single
// group a set of Animations into the same AnimationContainer by way of // AnimationContainer. You can group a set of Animations into the same
// Animation::SetContainer. Grouping a set of Animations into the same // AnimationContainer by way of Animation::SetContainer. Grouping a set of
// AnimationContainer ensures they all update and start at the same time. // Animations into the same AnimationContainer ensures they all update and start
// at the same time.
// //
// AnimationContainer is ref counted. Each Animation contained within the // AnimationContainer is ref counted. Each Animation contained within the
// AnimationContainer own it. // AnimationContainer own it.
...@@ -53,7 +55,13 @@ class ANIMATION_EXPORT AnimationContainer ...@@ -53,7 +55,13 @@ class ANIMATION_EXPORT AnimationContainer
// Are there any timers running? // Are there any timers running?
bool is_running() const { return !elements_.empty(); } bool is_running() const { return !elements_.empty(); }
void SetAnimationRunner(std::unique_ptr<AnimationRunner> runner);
bool has_custom_animation_runner() const {
return has_custom_animation_runner_;
}
private: private:
friend class AnimationContainerTestApi;
friend class base::RefCounted<AnimationContainer>; friend class base::RefCounted<AnimationContainer>;
// This set is usually quite small so a flat_set is the most obvious choice. // This set is usually quite small so a flat_set is the most obvious choice.
...@@ -67,7 +75,7 @@ class ANIMATION_EXPORT AnimationContainer ...@@ -67,7 +75,7 @@ class ANIMATION_EXPORT AnimationContainer
~AnimationContainer(); ~AnimationContainer();
// Timer callback method. // Timer callback method.
void Run(); void Run(base::TimeTicks current_time);
// Sets min_timer_interval_ and restarts the timer. // Sets min_timer_interval_ and restarts the timer.
void SetMinTimerInterval(base::TimeDelta delta); void SetMinTimerInterval(base::TimeDelta delta);
...@@ -80,7 +88,7 @@ class ANIMATION_EXPORT AnimationContainer ...@@ -80,7 +88,7 @@ class ANIMATION_EXPORT AnimationContainer
// . If only a single animation has been started and the timer hasn't yet // . If only a single animation has been started and the timer hasn't yet
// fired this is the time the animation was added. // fired this is the time the animation was added.
// . The time the last animation ran at (::Run was invoked). // . The time the last animation ran at (::Run was invoked).
base::TimeTicks last_tick_time_; base::TimeTicks last_tick_time_ = base::TimeTicks::Now();
// Set of elements (animations) being managed. // Set of elements (animations) being managed.
Elements elements_; Elements elements_;
...@@ -92,11 +100,13 @@ class ANIMATION_EXPORT AnimationContainer ...@@ -92,11 +100,13 @@ class ANIMATION_EXPORT AnimationContainer
// it means that the linear scan for the new minimum timer can almost always // it means that the linear scan for the new minimum timer can almost always
// be avoided. // be avoided.
base::TimeDelta min_timer_interval_; base::TimeDelta min_timer_interval_;
size_t min_timer_interval_count_; size_t min_timer_interval_count_ = 0;
base::RepeatingTimer timer_; std::unique_ptr<AnimationRunner> runner_ =
AnimationRunner::CreateDefaultAnimationRunner();
bool has_custom_animation_runner_ = false;
AnimationContainerObserver* observer_; AnimationContainerObserver* observer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AnimationContainer); DISALLOW_COPY_AND_ASSIGN(AnimationContainer);
}; };
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
namespace gfx { namespace gfx {
class Animation; class Animation;
class AnimationContainer;
// AnimationDelegate // AnimationDelegate
// //
...@@ -27,6 +28,10 @@ class ANIMATION_EXPORT AnimationDelegate { ...@@ -27,6 +28,10 @@ class ANIMATION_EXPORT AnimationDelegate {
// Called when an animation has been canceled. // Called when an animation has been canceled.
virtual void AnimationCanceled(const Animation* animation) {} virtual void AnimationCanceled(const Animation* animation) {}
// Called when an animation container has been set. This gives a chance to
// set a custom animation runner.
virtual void AnimationContainerWasSet(AnimationContainer* container) {}
}; };
} // namespace gfx } // namespace gfx
......
// Copyright 2019 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/gfx/animation/animation_runner.h"
#include <utility>
#include "base/timer/timer.h"
namespace {
// A default AnimationRunner based on base::Timer.
// TODO(https://crbug.com/953585): Remove this altogether.
class DefaultAnimationRunner : public gfx::AnimationRunner {
public:
DefaultAnimationRunner() = default;
~DefaultAnimationRunner() override = default;
// gfx::AnimationRunner:
void Stop() override { timer_.Stop(); }
protected:
// gfx::AnimationRunner:
void OnStart(base::TimeDelta min_interval) override {
timer_.Start(FROM_HERE, min_interval, this,
&DefaultAnimationRunner::OnTimerTick);
}
private:
void OnTimerTick() { Step(base::TimeTicks::Now()); }
base::RepeatingTimer timer_;
};
} // namespace
namespace gfx {
// static
std::unique_ptr<AnimationRunner>
AnimationRunner::CreateDefaultAnimationRunner() {
return std::make_unique<DefaultAnimationRunner>();
}
AnimationRunner::~AnimationRunner() = default;
void AnimationRunner::Start(
base::TimeDelta min_interval,
base::RepeatingCallback<void(base::TimeTicks)> step) {
step_ = std::move(step);
OnStart(min_interval);
}
AnimationRunner::AnimationRunner() = default;
void AnimationRunner::Step(base::TimeTicks tick) {
step_.Run(tick);
}
void AnimationRunner::SetAnimationTimeForTesting(base::TimeTicks time) {
step_.Run(time);
}
} // namespace gfx
// Copyright 2019 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.
#ifndef UI_GFX_ANIMATION_ANIMATION_RUNNER_H_
#define UI_GFX_ANIMATION_ANIMATION_RUNNER_H_
#include <memory>
#include "base/callback.h"
#include "base/time/time.h"
#include "ui/gfx/animation/animation_export.h"
namespace gfx {
// Interface for custom animation runner. CompositorAnimationRunner can control
// animation tick with this.
class ANIMATION_EXPORT AnimationRunner {
public:
// Creates a default AnimationRunner based on base::Timer. Ideally,
// we should prefer the compositor-based animation runner to this.
// TODO(https://crbug.com/953585): Remove this altogether.
static std::unique_ptr<AnimationRunner> CreateDefaultAnimationRunner();
AnimationRunner(const AnimationRunner&) = delete;
AnimationRunner& operator=(const AnimationRunner&) = delete;
virtual ~AnimationRunner();
// Sets the provided |step| callback, then calls OnStart() with the provided
// |min_interval| to allow the subclass to actually begin animating.
// Subclasses are expected to call Step() periodically to drive the animation.
void Start(base::TimeDelta min_interval,
base::RepeatingCallback<void(base::TimeTicks)> step);
// Called when subclasses don't need to call Step() anymore.
virtual void Stop() = 0;
protected:
AnimationRunner();
// Called when subclasses should start calling Step() periodically to
// drive the animation.
virtual void OnStart(base::TimeDelta min_interval) = 0;
// Advances the animation based on |tick|.
void Step(base::TimeTicks tick);
private:
friend class AnimationContainerTestApi;
// Advances the animation manually for testing.
void SetAnimationTimeForTesting(base::TimeTicks time);
base::RepeatingCallback<void(base::TimeTicks)> step_;
};
} // namespace gfx
#endif // UI_GFX_ANIMATION_ANIMATION_RUNNER_H_
...@@ -32,4 +32,17 @@ void AnimationTestApi::Step(base::TimeTicks ticks) { ...@@ -32,4 +32,17 @@ void AnimationTestApi::Step(base::TimeTicks ticks) {
animation_->Step(ticks); animation_->Step(ticks);
} }
AnimationContainerTestApi::AnimationContainerTestApi(
AnimationContainer* container)
: container_(container) {
container_->runner_->Stop();
}
AnimationContainerTestApi::~AnimationContainerTestApi() = default;
void AnimationContainerTestApi::IncrementTime(base::TimeDelta delta) {
container_->runner_->SetAnimationTimeForTesting(container_->last_tick_time() +
delta);
}
} // namespace gfx } // namespace gfx
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/macros.h" #include "base/macros.h"
#include "ui/gfx/animation/animation.h" #include "ui/gfx/animation/animation.h"
#include "ui/gfx/animation/animation_container.h"
#include "ui/gfx/animation/animation_export.h" #include "ui/gfx/animation/animation_export.h"
namespace gfx { namespace gfx {
...@@ -37,6 +38,22 @@ class AnimationTestApi { ...@@ -37,6 +38,22 @@ class AnimationTestApi {
DISALLOW_COPY_AND_ASSIGN(AnimationTestApi); DISALLOW_COPY_AND_ASSIGN(AnimationTestApi);
}; };
// For manual animation time control in tests. Creating this object will
// pause the AnimationRunner of |container| immediately.
class AnimationContainerTestApi {
public:
explicit AnimationContainerTestApi(AnimationContainer* container);
AnimationContainerTestApi(const AnimationContainerTestApi&) = delete;
AnimationContainerTestApi& operator=(const AnimationContainerTestApi&) =
delete;
~AnimationContainerTestApi();
void IncrementTime(base::TimeDelta delta);
private:
AnimationContainer* container_;
};
} // namespace gfx } // namespace gfx
#endif // UI_GFX_ANIMATION_ANIMATION_TEST_API_H_ #endif // UI_GFX_ANIMATION_ANIMATION_TEST_API_H_
...@@ -63,8 +63,10 @@ jumbo_component("views") { ...@@ -63,8 +63,10 @@ jumbo_component("views") {
"accessibility/view_accessibility.h", "accessibility/view_accessibility.h",
"accessibility/view_accessibility_utils.h", "accessibility/view_accessibility_utils.h",
"accessible_pane_view.h", "accessible_pane_view.h",
"animation/animation_delegate_views.h",
"animation/bounds_animator.h", "animation/bounds_animator.h",
"animation/bounds_animator_observer.h", "animation/bounds_animator_observer.h",
"animation/compositor_animation_runner.h",
"animation/flood_fill_ink_drop_ripple.h", "animation/flood_fill_ink_drop_ripple.h",
"animation/ink_drop.h", "animation/ink_drop.h",
"animation/ink_drop_animation_ended_reason.h", "animation/ink_drop_animation_ended_reason.h",
...@@ -284,7 +286,9 @@ jumbo_component("views") { ...@@ -284,7 +286,9 @@ jumbo_component("views") {
"accessibility/view_accessibility.cc", "accessibility/view_accessibility.cc",
"accessibility/view_accessibility_utils.cc", "accessibility/view_accessibility_utils.cc",
"accessible_pane_view.cc", "accessible_pane_view.cc",
"animation/animation_delegate_views.cc",
"animation/bounds_animator.cc", "animation/bounds_animator.cc",
"animation/compositor_animation_runner.cc",
"animation/flood_fill_ink_drop_ripple.cc", "animation/flood_fill_ink_drop_ripple.cc",
"animation/ink_drop.cc", "animation/ink_drop.cc",
"animation/ink_drop_animation_ended_reason.cc", "animation/ink_drop_animation_ended_reason.cc",
...@@ -1000,6 +1004,7 @@ test("views_unittests") { ...@@ -1000,6 +1004,7 @@ test("views_unittests") {
sources = [ sources = [
"accessible_pane_view_unittest.cc", "accessible_pane_view_unittest.cc",
"animation/bounds_animator_unittest.cc", "animation/bounds_animator_unittest.cc",
"animation/compositor_animation_runner_unittest.cc",
"animation/flood_fill_ink_drop_ripple_unittest.cc", "animation/flood_fill_ink_drop_ripple_unittest.cc",
"animation/ink_drop_highlight_unittest.cc", "animation/ink_drop_highlight_unittest.cc",
"animation/ink_drop_host_view_unittest.cc", "animation/ink_drop_host_view_unittest.cc",
......
// Copyright 2019 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/views/animation/animation_delegate_views.h"
#include "ui/gfx/animation/animation_container.h"
#include "ui/views/animation/compositor_animation_runner.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace views {
AnimationDelegateViews::AnimationDelegateViews(View* view) : view_(view) {
scoped_observer_.Add(view);
}
AnimationDelegateViews::~AnimationDelegateViews() = default;
void AnimationDelegateViews::AnimationContainerWasSet(
gfx::AnimationContainer* container) {
if (container_ == container)
return;
container_ = container;
UpdateAnimationRunner();
}
void AnimationDelegateViews::OnViewAddedToWidget(View* observed_view) {
UpdateAnimationRunner();
}
void AnimationDelegateViews::OnViewRemovedFromWidget(View* observed_view) {
UpdateAnimationRunner();
}
void AnimationDelegateViews::OnViewIsDeleting(View* observed_view) {
scoped_observer_.Remove(view_);
view_ = nullptr;
UpdateAnimationRunner();
}
void AnimationDelegateViews::UpdateAnimationRunner() {
if (!container_)
return;
if (!view_ || !view_->GetWidget()) {
// TODO(https://crbug.com/960621): make sure the container has a correct
// compositor-assisted runner.
container_->SetAnimationRunner(nullptr);
return;
}
if (container_->has_custom_animation_runner())
return;
container_->SetAnimationRunner(std::make_unique<CompositorAnimationRunner>(
view_->GetWidget()->GetCompositor()));
}
} // namespace views
// Copyright 2019 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.
#ifndef UI_VIEWS_ANIMATION_ANIMATION_DELEGATE_VIEWS_H_
#define UI_VIEWS_ANIMATION_ANIMATION_DELEGATE_VIEWS_H_
#include <memory>
#include "base/scoped_observer.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/views/view_observer.h"
#include "ui/views/views_export.h"
namespace views {
class View;
// Provides default implementaton to adapt CompositorAnimationRunner for
// Animation.
class VIEWS_EXPORT AnimationDelegateViews : public gfx::AnimationDelegate,
public ViewObserver {
public:
explicit AnimationDelegateViews(View* view);
~AnimationDelegateViews() override;
// gfx::AnimationDelegate:
void AnimationContainerWasSet(gfx::AnimationContainer* container) override;
// ViewObserver:
void OnViewAddedToWidget(View* observed_view) final;
void OnViewRemovedFromWidget(View* observed_view) final;
void OnViewIsDeleting(View* observed_view) final;
gfx::AnimationContainer* container() { return container_; }
private:
// Sets CompositorAnimationRunner to |container_| if possible. Otherwise,
// clears AnimationRunner of |container_|.
void UpdateAnimationRunner();
View* view_;
gfx::AnimationContainer* container_ = nullptr;
ScopedObserver<View, AnimationDelegateViews> scoped_observer_{this};
};
} // namespace views
#endif // UI_VIEWS_ANIMATION_ANIMATION_DELEGATE_VIEWS_H_
...@@ -10,11 +10,14 @@ ...@@ -10,11 +10,14 @@
#include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/animation/slide_animation.h"
#include "ui/views/animation/bounds_animator_observer.h" #include "ui/views/animation/bounds_animator_observer.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace views { namespace views {
BoundsAnimator::BoundsAnimator(View* parent) BoundsAnimator::BoundsAnimator(View* parent)
: parent_(parent), container_(new gfx::AnimationContainer()) { : AnimationDelegateViews(parent),
parent_(parent),
container_(new gfx::AnimationContainer()) {
container_->set_observer(this); container_->set_observer(this);
} }
......
...@@ -12,10 +12,11 @@ ...@@ -12,10 +12,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/observer_list.h" #include "base/observer_list.h"
#include "ui/gfx/animation/animation_container.h"
#include "ui/gfx/animation/animation_container_observer.h" #include "ui/gfx/animation/animation_container_observer.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/tween.h" #include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/views/views_export.h" #include "ui/views/views_export.h"
namespace gfx { namespace gfx {
...@@ -37,7 +38,7 @@ class View; ...@@ -37,7 +38,7 @@ class View;
// You can attach an AnimationDelegate to the individual animation for a view // You can attach an AnimationDelegate to the individual animation for a view
// by way of SetAnimationDelegate. Additionally you can attach an observer to // by way of SetAnimationDelegate. Additionally you can attach an observer to
// the BoundsAnimator that is notified when all animations are complete. // the BoundsAnimator that is notified when all animations are complete.
class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews,
public gfx::AnimationContainerObserver { public gfx::AnimationContainerObserver {
public: public:
explicit BoundsAnimator(View* view); explicit BoundsAnimator(View* view);
...@@ -99,6 +100,8 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, ...@@ -99,6 +100,8 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
void AddObserver(BoundsAnimatorObserver* observer); void AddObserver(BoundsAnimatorObserver* observer);
void RemoveObserver(BoundsAnimatorObserver* observer); void RemoveObserver(BoundsAnimatorObserver* observer);
gfx::AnimationContainer* container() { return container_.get(); }
protected: protected:
// Creates the animation to use for animating views. // Creates the animation to use for animating views.
virtual std::unique_ptr<gfx::SlideAnimation> CreateAnimation(); virtual std::unique_ptr<gfx::SlideAnimation> CreateAnimation();
...@@ -151,7 +154,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate, ...@@ -151,7 +154,7 @@ class VIEWS_EXPORT BoundsAnimator : public gfx::AnimationDelegate,
void AnimationEndedOrCanceled(const gfx::Animation* animation, void AnimationEndedOrCanceled(const gfx::Animation* animation,
AnimationEndType type); AnimationEndType type);
// gfx::AnimationDelegate overrides. // AnimationDelegateViews overrides.
void AnimationProgressed(const gfx::Animation* animation) override; void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override; void AnimationEnded(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override; void AnimationCanceled(const gfx::Animation* animation) override;
......
// Copyright 2019 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/views/animation/compositor_animation_runner.h"
#include "ui/compositor/compositor.h"
namespace views {
////////////////////////////////////////////////////////////////////////////////
// CompositorAnimationRunner::CompositorChecker
//
CompositorAnimationRunner::CompositorChecker::CompositorChecker(
CompositorAnimationRunner* runner)
: runner_(runner) {
scoped_observer_.Add(runner_->compositor_);
}
CompositorAnimationRunner::CompositorChecker::~CompositorChecker() = default;
void CompositorAnimationRunner::CompositorChecker::OnCompositingShuttingDown(
ui::Compositor* compositor) {
runner_->OnCompositingShuttingDown(compositor);
DCHECK(!compositor->HasAnimationObserver(runner_));
scoped_observer_.Remove(compositor);
}
////////////////////////////////////////////////////////////////////////////////
// CompositorAnimationRunner
//
CompositorAnimationRunner::CompositorAnimationRunner(ui::Compositor* compositor)
: compositor_(compositor) {
DCHECK(compositor_);
}
CompositorAnimationRunner::~CompositorAnimationRunner() {
// Make sure we're not observing |compositor_|.
Stop();
DCHECK(!compositor_ || !compositor_->HasAnimationObserver(this));
}
void CompositorAnimationRunner::Stop() {
if (compositor_ && compositor_->HasAnimationObserver(this))
compositor_->RemoveAnimationObserver(this);
min_interval_ = base::TimeDelta::Max();
}
void CompositorAnimationRunner::OnAnimationStep(base::TimeTicks timestamp) {
if (timestamp - last_tick_ < min_interval_)
return;
last_tick_ = timestamp;
Step(last_tick_);
}
void CompositorAnimationRunner::OnCompositingShuttingDown(
ui::Compositor* compositor) {
Stop();
compositor_ = nullptr;
}
void CompositorAnimationRunner::OnStart(base::TimeDelta min_interval) {
if (!compositor_)
return;
min_interval_ = min_interval;
DCHECK(!compositor_->HasAnimationObserver(this));
compositor_->AddAnimationObserver(this);
}
} // namespace views
// Copyright 2019 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.
#ifndef UI_VIEWS_ANIMATION_COMPOSITOR_ANIMATION_RUNNER_H_
#define UI_VIEWS_ANIMATION_COMPOSITOR_ANIMATION_RUNNER_H_
#include "base/scoped_observer.h"
#include "base/time/time.h"
#include "ui/compositor/compositor_animation_observer.h"
#include "ui/compositor/compositor_observer.h"
#include "ui/gfx/animation/animation_container.h"
namespace ui {
class Compositor;
} // namespace ui
namespace views {
// An animation runner based on ui::Compositor.
class CompositorAnimationRunner : public gfx::AnimationRunner,
public ui::CompositorAnimationObserver {
public:
explicit CompositorAnimationRunner(ui::Compositor* compositor);
~CompositorAnimationRunner() override;
// gfx::AnimationRunner:
void Stop() override;
// ui::CompositorAnimationObserver:
void OnAnimationStep(base::TimeTicks timestamp) override;
void OnCompositingShuttingDown(ui::Compositor* compositor) override;
protected:
// gfx::AnimationRunner:
void OnStart(base::TimeDelta min_interval) override;
private:
// This observes Compositor's destruction and helps CompositorAnimationRunner
// to stop animation. we need this because CompositorAnimationRunner observes
// the Compositor only when it's animating.
class CompositorChecker : public ui::CompositorObserver {
public:
explicit CompositorChecker(CompositorAnimationRunner* runner);
CompositorChecker(const CompositorChecker& other) = delete;
CompositorChecker& operator=(const CompositorChecker& other) = delete;
~CompositorChecker() override;
// ui::CompositorObserver:
void OnCompositingShuttingDown(ui::Compositor* compositor) override;
private:
CompositorAnimationRunner* runner_;
ScopedObserver<ui::Compositor, CompositorChecker> scoped_observer_{this};
};
// When |compositor_| is nullptr, it means compositor has been shut down.
ui::Compositor* compositor_;
base::TimeDelta min_interval_ = base::TimeDelta::Max();
base::TimeTicks last_tick_;
CompositorChecker checker_{this};
};
} // namespace views
#endif // UI_VIEWS_ANIMATION_COMPOSITOR_ANIMATION_RUNNER_H_
// Copyright 2019 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/views/animation/compositor_animation_runner.h"
#include "base/test/bind_test_util.h"
#include "base/timer/timer.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/views/test/widget_test.h"
namespace views {
namespace test {
using CompositorAnimationRunnerTest = WidgetTest;
TEST_F(CompositorAnimationRunnerTest, BasicCoverageTest) {
WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget());
widget->Show();
constexpr base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(100);
AnimationDelegateViews delegate(widget->GetContentsView());
gfx::LinearAnimation animation(
kDuration, gfx::LinearAnimation::kDefaultFrameRate, &delegate);
base::RepeatingTimer interval_timer;
base::RunLoop run_loop;
animation.Start();
EXPECT_TRUE(animation.is_animating());
EXPECT_TRUE(delegate.container()->has_custom_animation_runner());
interval_timer.Start(FROM_HERE, kDuration, base::BindLambdaForTesting([&]() {
if (animation.is_animating())
return;
interval_timer.Stop();
run_loop.Quit();
}));
run_loop.Run();
}
} // namespace test
} // namespace views
...@@ -2158,8 +2158,11 @@ void View::PropagateRemoveNotifications(View* old_parent, ...@@ -2158,8 +2158,11 @@ void View::PropagateRemoveNotifications(View* old_parent,
for (View* v = this; v; v = v->parent_) for (View* v = this; v; v = v->parent_)
v->ViewHierarchyChangedImpl(true, details); v->ViewHierarchyChangedImpl(true, details);
if (is_removed_from_widget) if (is_removed_from_widget) {
RemovedFromWidget(); RemovedFromWidget();
for (ViewObserver& observer : observers_)
observer.OnViewRemovedFromWidget(this);
}
} }
void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details, void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details,
...@@ -2170,8 +2173,11 @@ void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details, ...@@ -2170,8 +2173,11 @@ void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details,
child->PropagateAddNotifications(details, is_added_to_widget); child->PropagateAddNotifications(details, is_added_to_widget);
} }
ViewHierarchyChangedImpl(true, details); ViewHierarchyChangedImpl(true, details);
if (is_added_to_widget) if (is_added_to_widget) {
AddedToWidget(); AddedToWidget();
for (ViewObserver& observer : observers_)
observer.OnViewAddedToWidget(this);
}
} }
void View::PropagateNativeViewHierarchyChanged() { void View::PropagateNativeViewHierarchyChanged() {
......
...@@ -39,6 +39,12 @@ class VIEWS_EXPORT ViewObserver { ...@@ -39,6 +39,12 @@ class VIEWS_EXPORT ViewObserver {
View* observed_view, View* observed_view,
const ViewHierarchyChangedDetails& details) {} const ViewHierarchyChangedDetails& details) {}
// Called when View::AddedToWidget() is called.
virtual void OnViewAddedToWidget(View* observed_view) {}
// Called when View::RemovedFromWidget() is called.
virtual void OnViewRemovedFromWidget(View* observed_view) {}
// Called when a child is reordered among its siblings, specifically // Called when a child is reordered among its siblings, specifically
// View::ReorderChildView() is called on |observed_view|. // View::ReorderChildView() is called on |observed_view|.
virtual void OnChildViewReordered(View* observed_view, View* child) {} virtual void OnChildViewReordered(View* observed_view, View* child) {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment