Commit e6713881 authored by Mitsuru Oshima's avatar Mitsuru Oshima Committed by Commit Bot

Reland "Chrome OS: Use layer animation browser window frame activation animation."

This reverts commit 20aca0dd.

Reason for revert: The issue has been resoved in crrev.com/c/2384070

Original change's description:
> Revert "Chrome OS: Use layer animation browser window frame activation animation."
> 
> This reverts commit 3a2299e5.
> 
> Reason for revert: Suspected breaking https://ci.chromium.org/p/chromium/builders/ci/Linux%20Chromium%20OS%20ASan%20LSan%20Tests%20%281%29/38374 and https://ci.chromium.org/p/chromium/builders/ci/linux-chromeos-dbg/20193
> 
> Original change's description:
> > Chrome OS: Use layer animation browser window frame activation animation.
> > 
> > This is a revised version of original attempt crrev.comc/c/2166804.
> > Instead of creating a new layers, this clone and animate the window's layer,
> > but makes sure that the animating layer is at the bottom of other layers
> > within the window.
> > 
> > * It adds a invisible view with a layer
> >   at the bottom of the non_client_view (no frame w/o this, so we can exclude such case).
> > * Adds the animating layer as a child of this view, with
> >   "SetMasksToBounds(false)" so that it will not be clipped during animation.
> > * Once animation is done, this animating, old layer will be removed.
> > 
> > Bug: 1073685
> > Test: covered by unittests.
> > Change-Id: Ib5ae6a218d200394c54932e5b559217fcc7f89e1
> > Fixed: 2
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2375686
> > Reviewed-by: Scott Violet <sky@chromium.org>
> > Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#802386}
> 
> TBR=sky@chromium.org,oshima@chromium.org
> 
> Change-Id: I10600b9bc69818257f66fb5c1ae86f7fd1119c75
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: 1073685
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2381217
> Reviewed-by: Melissa Zhang <melzhang@chromium.org>
> Commit-Queue: Melissa Zhang <melzhang@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#802539}

TBR=sky@chromium.org,oshima@chromium.org,melzhang@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: 1073685
Change-Id: I38725fa15f79576db154db91f7ca384578f545b1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2385815Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803143}
parent 04bea739
......@@ -6,6 +6,7 @@
#include <memory>
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/public/cpp/caption_buttons/frame_back_button.h"
#include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
#include "ash/public/cpp/shell_window_ids.h"
......@@ -13,13 +14,16 @@
#include "ash/test/ash_test_base.h"
#include "ash/wm/desks/desks_util.h"
#include "base/i18n/rtl.h"
#include "base/stl_util.h"
#include "base/test/icu_test_util.h"
#include "ui/aura/window.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/gfx/animation/animation_test_api.h"
#include "ui/gfx/color_utils.h"
#include "ui/views/test/test_views.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"
#include "ui/wm/core/window_util.h"
using views::NonClientFrameView;
using views::Widget;
......@@ -82,70 +86,197 @@ TEST_F(DefaultFrameHeaderTest, MinimumHeaderWidthRTL) {
// Ensure the right frame colors are used.
TEST_F(DefaultFrameHeaderTest, FrameColors) {
std::unique_ptr<Widget> widget = CreateTestWidget(
nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(1, 2, 3, 4));
FrameCaptionButtonContainerView container(widget.get());
views::StaticSizedView window_icon(gfx::Size(16, 16));
window_icon.SetBounds(0, 0, 16, 16);
widget->SetBounds(gfx::Rect(0, 0, 500, 500));
widget->Show();
DefaultFrameHeader frame_header(
widget.get(), widget->non_client_view()->frame_view(), &container);
const auto win0_bounds = gfx::Rect{1, 2, 3, 4};
auto win0 = CreateAppWindow(win0_bounds, AppType::BROWSER);
Widget* widget = Widget::GetWidgetForNativeWindow(win0.get());
DefaultFrameHeader* frame_header =
static_cast<DefaultFrameHeader*>(FrameHeader::Get(widget));
// Check frame color is sensitive to mode.
SkColor active = SkColorSetRGB(70, 70, 70);
SkColor inactive = SkColorSetRGB(200, 200, 200);
widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, active);
widget->GetNativeWindow()->SetProperty(kFrameInactiveColorKey, inactive);
frame_header.UpdateFrameColors();
frame_header.mode_ = FrameHeader::MODE_ACTIVE;
EXPECT_EQ(active, frame_header.GetCurrentFrameColor());
frame_header.mode_ = FrameHeader::MODE_INACTIVE;
EXPECT_EQ(inactive, frame_header.GetCurrentFrameColor());
EXPECT_EQ(active, frame_header.GetActiveFrameColorForPaintForTest());
win0->SetProperty(kFrameActiveColorKey, active);
win0->SetProperty(kFrameInactiveColorKey, inactive);
frame_header->UpdateFrameColors();
frame_header->mode_ = FrameHeader::MODE_ACTIVE;
EXPECT_EQ(active, frame_header->GetCurrentFrameColor());
frame_header->mode_ = FrameHeader::MODE_INACTIVE;
EXPECT_EQ(inactive, frame_header->GetCurrentFrameColor());
EXPECT_EQ(active, frame_header->GetActiveFrameColorForPaintForTest());
// Update to the new value which has no blue, which should animate.
frame_header.mode_ = FrameHeader::MODE_ACTIVE;
frame_header->mode_ = FrameHeader::MODE_ACTIVE;
SkColor new_active = SkColorSetRGB(70, 70, 0);
widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_active);
frame_header.UpdateFrameColors();
win0->SetProperty(kFrameActiveColorKey, new_active);
frame_header->UpdateFrameColors();
gfx::SlideAnimation* animation =
frame_header.GetAnimationForActiveFrameColorForTest();
gfx::AnimationTestApi test_api(animation);
// Now update to the new value which is full blue.
SkColor new_new_active = SkColorSetRGB(70, 70, 255);
win0->SetProperty(kFrameActiveColorKey, new_new_active);
frame_header->UpdateFrameColors();
// animate half way through.
base::TimeTicks now = base::TimeTicks::Now();
test_api.SetStartTime(now);
test_api.Step(now + base::TimeDelta::FromMilliseconds(120));
// Again, GetCurrentFrameColor should return the target color.
EXPECT_EQ(new_new_active, frame_header->GetCurrentFrameColor());
}
// GetCurrentFrameColor should return the target color.
EXPECT_EQ(new_active, frame_header.GetCurrentFrameColor());
namespace {
// The color used for paint should be somewhere between 0 and 70.
SkColor new_active_for_paint =
frame_header.GetActiveFrameColorForPaintForTest();
EXPECT_NE(new_active, new_active_for_paint);
EXPECT_EQ(53u, SkColorGetB(new_active_for_paint));
class LayerDestroyedChecker : public ui::LayerObserver {
public:
explicit LayerDestroyedChecker(ui::Layer* layer) { layer->AddObserver(this); }
LayerDestroyedChecker(const LayerDestroyedChecker&) = delete;
LayerDestroyedChecker& operator=(const LayerDestroyedChecker&) = delete;
~LayerDestroyedChecker() override = default;
// Now update to the new value which is full blue.
SkColor new_new_active = SkColorSetRGB(70, 70, 255);
widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_new_active);
frame_header.UpdateFrameColors();
void LayerDestroyed(ui::Layer* layer) override {
layer->RemoveObserver(this);
destroyed_ = true;
}
bool destroyed() const { return destroyed_; }
now = base::TimeTicks::Now();
test_api.SetStartTime(now);
test_api.Step(now + base::TimeDelta::FromMilliseconds(20));
private:
bool destroyed_ = false;
};
// Again, GetCurrentFrameColor should return the target color.
EXPECT_EQ(new_new_active, frame_header.GetCurrentFrameColor());
// The start value should be the previous paint color, so it should be
// near 53.
SkColor new_new_active_for_paint =
frame_header.GetActiveFrameColorForPaintForTest();
EXPECT_NE(new_active_for_paint, new_new_active_for_paint);
EXPECT_EQ(54u, SkColorGetB(new_new_active_for_paint));
} // namespace
// A class to wait until hthe frame header is painted.
class FramePaintWaiter : public ui::CompositorObserver {
public:
explicit FramePaintWaiter(aura::Window* window)
: frame_header_(
FrameHeader::Get(Widget::GetWidgetForNativeWindow(window))) {
frame_header_->view()->GetWidget()->GetCompositor()->AddObserver(this);
}
FramePaintWaiter(const FramePaintWaiter&) = delete;
FramePaintWaiter& operator=(FramePaintWaiter&) = delete;
~FramePaintWaiter() override {
frame_header_->view()->GetWidget()->GetCompositor()->RemoveObserver(this);
}
// ui::CompositorObserver:
void OnCompositingDidCommit(ui::Compositor* compositor) override {
if (frame_header_->painted_)
run_loop_.Quit();
}
void Wait() { run_loop_.Run(); }
private:
base::RunLoop run_loop_;
FrameHeader* frame_header_ = nullptr;
};
TEST_F(DefaultFrameHeaderTest, DeleteDuringAnimation) {
const auto bounds = gfx::Rect(100, 100);
auto win0 = CreateAppWindow(bounds, AppType::BROWSER);
auto win1 = CreateAppWindow(bounds, AppType::BROWSER);
Widget* widget = Widget::GetWidgetForNativeWindow(win0.get());
EXPECT_TRUE(FrameHeader::Get(widget));
EXPECT_TRUE(wm::IsActiveWindow(win1.get()));
// A frame will not animate until it is painted first.
FramePaintWaiter(win0.get()).Wait();
FramePaintWaiter(win1.get()).Wait();
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
wm::ActivateWindow(win0.get());
auto* header_view = NonClientFrameViewAsh::Get(win0.get())->GetHeaderView();
ASSERT_TRUE(header_view);
auto* animating_layer_holding_view = header_view->children()[0];
EXPECT_TRUE(!std::strcmp(animating_layer_holding_view->GetClassName(),
"FrameAnimatorView"));
ASSERT_TRUE(animating_layer_holding_view->layer());
ASSERT_GT(animating_layer_holding_view->layer()->parent()->children().size(),
2u);
auto* animating_layer =
animating_layer_holding_view->layer()->parent()->children()[0];
EXPECT_EQ(ui::LAYER_TEXTURED, animating_layer->type());
EXPECT_NE(std::string::npos, animating_layer->name().find(":Old", 0));
EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
LayerDestroyedChecker checker(animating_layer);
win0.reset();
EXPECT_TRUE(checker.destroyed());
}
// Make sure that the animation is canceled when resized.
TEST_F(DefaultFrameHeaderTest, ResizeAndReorderDuringAnimation) {
const auto bounds = gfx::Rect(100, 100);
auto win_0 = CreateAppWindow(bounds, AppType::BROWSER);
auto win_1 = CreateAppWindow(bounds, AppType::BROWSER);
EXPECT_TRUE(wm::IsActiveWindow(win_1.get()));
// A frame will not animate until it is painted first.
FramePaintWaiter(win_0.get()).Wait();
FramePaintWaiter(win_1.get()).Wait();
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
auto* header_view_0 =
NonClientFrameViewAsh::Get(win_0.get())->GetHeaderView();
auto* animating_layer_holding_view_0 = header_view_0->children()[0];
EXPECT_TRUE(!std::strcmp(animating_layer_holding_view_0->GetClassName(),
"FrameAnimatorView"));
size_t original_layers_count_0 =
animating_layer_holding_view_0->layer()->parent()->children().size();
auto* header_view_1 =
NonClientFrameViewAsh::Get(win_1.get())->GetHeaderView();
auto* animating_layer_holding_view_1 = header_view_1->children()[0];
EXPECT_TRUE(!std::strcmp(animating_layer_holding_view_1->GetClassName(),
"FrameAnimatorView"));
size_t original_layers_count_1 =
animating_layer_holding_view_1->layer()->parent()->children().size();
wm::ActivateWindow(win_0.get());
{
// Resize during animation
EXPECT_EQ(
animating_layer_holding_view_0->layer()->parent()->children().size(),
original_layers_count_0 + 1);
auto* animating_layer =
animating_layer_holding_view_0->layer()->parent()->children()[0];
EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
LayerDestroyedChecker checker(animating_layer);
win_0->SetBounds(gfx::Rect(200, 200));
// Animating layer shuld have been removed.
EXPECT_EQ(
animating_layer_holding_view_0->layer()->parent()->children().size(),
original_layers_count_0);
EXPECT_TRUE(checker.destroyed());
}
{
// wind_1 should still be animating.
EXPECT_EQ(
animating_layer_holding_view_1->layer()->parent()->children().size(),
original_layers_count_1 + 1);
auto* animating_layer =
animating_layer_holding_view_1->layer()->parent()->children()[0];
EXPECT_TRUE(animating_layer->GetAnimator()->is_animating());
LayerDestroyedChecker checker(animating_layer);
// Change the view's stacking order should stop the animation.
ASSERT_EQ(3u, header_view_1->children().size());
header_view_1->ReorderChildView(header_view_1->children()[2], 0);
EXPECT_EQ(
animating_layer_holding_view_1->layer()->parent()->children().size(),
original_layers_count_1);
EXPECT_TRUE(checker.destroyed());
}
}
} // namespace ash
......@@ -291,13 +291,7 @@ void HeaderView::PaintHeaderContent(gfx::Canvas* canvas) {
if (!should_paint_ || !target_widget_)
return;
bool paint_as_active =
target_widget_->non_client_view()->frame_view()->ShouldPaintAsActive();
frame_header_->SetPaintAsActive(paint_as_active);
FrameHeader::Mode header_mode =
paint_as_active ? FrameHeader::MODE_ACTIVE : FrameHeader::MODE_INACTIVE;
frame_header_->PaintHeader(canvas, header_mode);
frame_header_->PaintHeader(canvas);
}
void HeaderView::UpdateBackButton() {
......
......@@ -410,8 +410,7 @@ NonClientFrameViewAsh::GetFrameCaptionButtonContainerViewForTest() {
}
void NonClientFrameViewAsh::PaintAsActiveChanged() {
// The icons differ between active and inactive.
header_view_->SchedulePaint();
header_view_->GetFrameHeader()->SetPaintAsActive(ShouldPaintAsActive());
frame_->non_client_view()->Layout();
}
......
......@@ -228,6 +228,7 @@ void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
minimize_button_->set_paint_as_active(paint_as_active);
size_button_->set_paint_as_active(paint_as_active);
close_button_->set_paint_as_active(paint_as_active);
SchedulePaint();
}
void FrameCaptionButtonContainerView::SetBackgroundColor(
......
......@@ -25,6 +25,10 @@ using views::Widget;
namespace {
// Duration of animation scheduled when frame color is changed.
constexpr base::TimeDelta kFrameColorChangeAnimationDuration =
base::TimeDelta::FromMilliseconds(240);
// Tiles an image into an area, rounding the top corners.
void TileRoundRect(gfx::Canvas* canvas,
const cc::PaintFlags& flags,
......@@ -53,36 +57,6 @@ void TileRoundRect(gfx::Canvas* canvas,
namespace ash {
DefaultFrameHeader::ColorAnimator::ColorAnimator(
gfx::AnimationDelegate* delegate)
: animation_(delegate) {
animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(240));
animation_.SetTweenType(gfx::Tween::EASE_IN);
animation_.Reset(1);
}
DefaultFrameHeader::ColorAnimator::ColorAnimator::~ColorAnimator() = default;
void DefaultFrameHeader::ColorAnimator::SetTargetColor(SkColor target) {
target_color_ = target;
start_color_ = current_color_;
if (current_color_ == kDefaultFrameColor) {
// Changing from default should be set immediately.
current_color_ = target_color_;
animation_.Reset(1);
} else {
animation_.Reset(0);
}
animation_.Show();
}
SkColor DefaultFrameHeader::ColorAnimator::GetCurrentColor() {
current_color_ =
color_utils::AlphaBlend(target_color_, start_color_,
static_cast<float>(animation_.GetCurrentValue()));
return current_color_;
}
///////////////////////////////////////////////////////////////////////////////
// DefaultFrameHeader, public:
......@@ -90,9 +64,7 @@ DefaultFrameHeader::DefaultFrameHeader(
views::Widget* target_widget,
views::View* header_view,
FrameCaptionButtonContainerView* caption_button_container)
: FrameHeader(target_widget, header_view),
active_frame_color_(this),
inactive_frame_color_(this) {
: FrameHeader(target_widget, header_view) {
DCHECK(caption_button_container);
SetCaptionButtonContainer(caption_button_container);
}
......@@ -114,18 +86,19 @@ void DefaultFrameHeader::UpdateFrameColors() {
target_window->GetProperty(kFrameInactiveColorKey);
bool updated = false;
if (active_frame_color_.target_color() != active_frame_color) {
active_frame_color_.SetTargetColor(active_frame_color);
updated = true;
// Update the frame if the frame color for the current active state chagnes.
if (active_frame_color_ != active_frame_color) {
active_frame_color_ = active_frame_color;
updated = mode() == Mode::MODE_ACTIVE;
}
if (inactive_frame_color_.target_color() != inactive_frame_color) {
inactive_frame_color_.SetTargetColor(inactive_frame_color);
updated = true;
if (inactive_frame_color_ != inactive_frame_color) {
inactive_frame_color_ = inactive_frame_color;
updated |= mode() == Mode::MODE_INACTIVE;
}
if (updated) {
UpdateCaptionButtonColors();
view()->SchedulePaint();
StartTransitionAnimation(kFrameColorChangeAnimationDuration);
}
}
......@@ -139,10 +112,8 @@ void DefaultFrameHeader::DoPaintHeader(gfx::Canvas* canvas) {
: 0;
cc::PaintFlags flags;
flags.setColor(color_utils::AlphaBlend(
active_frame_color_.GetCurrentColor(),
inactive_frame_color_.GetCurrentColor(),
static_cast<float>(activation_animation().GetCurrentValue())));
flags.setColor(mode() == Mode::MODE_ACTIVE ? active_frame_color_
: inactive_frame_color_);
flags.setAntiAlias(true);
if (width_in_pixels_ > 0) {
canvas->Save();
......@@ -186,17 +157,11 @@ aura::Window* DefaultFrameHeader::GetTargetWindow() {
}
SkColor DefaultFrameHeader::GetCurrentFrameColor() const {
return mode() == MODE_ACTIVE ? active_frame_color_.target_color()
: inactive_frame_color_.target_color();
}
gfx::SlideAnimation*
DefaultFrameHeader::GetAnimationForActiveFrameColorForTest() {
return active_frame_color_.animation();
return mode() == MODE_ACTIVE ? active_frame_color_ : inactive_frame_color_;
}
SkColor DefaultFrameHeader::GetActiveFrameColorForPaintForTest() {
return active_frame_color_.GetCurrentColor();
return active_frame_color_;
}
} // namespace ash
......@@ -26,12 +26,8 @@ class ASH_PUBLIC_EXPORT DefaultFrameHeader : public FrameHeader {
FrameCaptionButtonContainerView* caption_button_container);
~DefaultFrameHeader() override;
SkColor active_frame_color_for_testing() {
return active_frame_color_.target_color();
}
SkColor inactive_frame_color_for_testing() {
return inactive_frame_color_.target_color();
}
SkColor active_frame_color_for_testing() { return active_frame_color_; }
SkColor inactive_frame_color_for_testing() { return inactive_frame_color_; }
void SetWidthInPixels(int width_in_pixels);
......@@ -51,33 +47,10 @@ class ASH_PUBLIC_EXPORT DefaultFrameHeader : public FrameHeader {
// Returns the window of the target widget.
aura::Window* GetTargetWindow();
gfx::SlideAnimation* GetAnimationForActiveFrameColorForTest();
SkColor GetActiveFrameColorForPaintForTest();
// A utility class to animate color value.
class ColorAnimator {
public:
explicit ColorAnimator(gfx::AnimationDelegate* delegate);
~ColorAnimator();
void SetTargetColor(SkColor target);
SkColor target_color() const { return target_color_; }
SkColor GetCurrentColor();
float get_value() const { return animation_.GetCurrentValue(); }
gfx::SlideAnimation* animation() { return &animation_; }
private:
gfx::SlideAnimation animation_;
SkColor start_color_ = kDefaultFrameColor;
SkColor target_color_ = kDefaultFrameColor;
SkColor current_color_ = kDefaultFrameColor;
DISALLOW_COPY_AND_ASSIGN(ColorAnimator);
};
ColorAnimator active_frame_color_;
ColorAnimator inactive_frame_color_;
SkColor active_frame_color_ = kDefaultFrameColor;
SkColor inactive_frame_color_ = kDefaultFrameColor;
int width_in_pixels_ = -1;
......
......@@ -11,6 +11,9 @@
#include "ash/public/cpp/window_properties.h"
#include "base/logging.h" // DCHECK
#include "ui/base/class_property.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/font_list.h"
......@@ -29,6 +32,9 @@ namespace ash {
namespace {
constexpr base::TimeDelta kFrameActivationAnimationDuration =
base::TimeDelta::FromMilliseconds(200);
DEFINE_UI_CLASS_PROPERTY_KEY(FrameHeader*, kFrameHeaderKey, nullptr)
// Returns the available bounds for the header's title given the views to the
......@@ -61,29 +67,6 @@ gfx::Rect GetAvailableTitleBounds(const views::View* left_view,
return gfx::Rect(x, y, width, title_height);
}
// Returns true if the header for |widget| can animate to new visuals when the
// widget's activation changes. Returns false if the header should switch to
// new visuals instantaneously.
bool CanAnimateActivation(views::Widget* widget) {
// Do not animate the header if the parent (e.g. the active desk container) is
// already animating. All of the implementers of FrameHeader animate
// activation by continuously painting during the animation. This gives the
// parent's animation a slower frame rate.
// TODO(sky): Expose a better way to determine this rather than assuming the
// parent is a toplevel container.
aura::Window* window = widget->GetNativeWindow();
// TODO(sky): parent()->layer() is for mash until animations ported.
if (!window || !window->parent() || !window->parent()->layer())
return true;
ui::LayerAnimator* parent_layer_animator =
window->parent()->layer()->GetAnimator();
return !parent_layer_animator->IsAnimatingProperty(
ui::LayerAnimationElement::OPACITY) &&
!parent_layer_animator->IsAnimatingProperty(
ui::LayerAnimationElement::VISIBILITY);
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
......@@ -107,29 +90,102 @@ int FrameHeader::GetMinimumHeaderWidth() const {
caption_button_container_->GetMinimumSize().width();
}
void FrameHeader::PaintHeader(gfx::Canvas* canvas, Mode mode) {
Mode old_mode = mode_;
mode_ = mode;
if (mode_ != old_mode) {
UpdateCaptionButtonColors();
if (!initial_paint_ && CanAnimateActivation(target_widget_)) {
activation_animation_.SetSlideDuration(
base::TimeDelta::FromMilliseconds(200));
if (mode_ == MODE_ACTIVE)
activation_animation_.Show();
else
activation_animation_.Hide();
} else {
if (mode_ == MODE_ACTIVE)
activation_animation_.Reset(1);
else
activation_animation_.Reset(0);
// An invisible view that drives the frame's animation. This holds the animating
// layer as a layer beneath this view so that it's behind all other child layers
// of the window to avoid hiding their contents.
class FrameHeader::FrameAnimatorView : public views::View,
public views::ViewObserver,
public ui::ImplicitAnimationObserver {
public:
FrameAnimatorView(FrameHeader* frame_header, views::View* parent)
: frame_header_(frame_header), parent_(parent) {
SetPaintToLayer(ui::LAYER_NOT_DRAWN);
parent_->AddChildViewAt(this, 0);
parent_->AddObserver(this);
}
FrameAnimatorView(const FrameAnimatorView&) = delete;
FrameAnimatorView& operator=(const FrameAnimatorView&) = delete;
~FrameAnimatorView() override {
StopAnimation();
// A child view should always be removed first.
parent_->RemoveObserver(this);
}
void StartAnimation(base::TimeDelta duration) {
StopAnimation();
aura::Window* window = frame_header_->target_widget()->GetNativeWindow();
// Make sure the this view is at the bottom of root view's children.
parent_->ReorderChildView(this, 0);
std::unique_ptr<ui::LayerTreeOwner> old_layer_owner =
std::make_unique<ui::LayerTreeOwner>(window->RecreateLayer());
ui::Layer* old_layer = old_layer_owner->root();
ui::Layer* new_layer = window->layer();
new_layer->SetName(old_layer->name());
old_layer->SetName(old_layer->name() + ":Old");
old_layer->SetTransform(gfx::Transform());
layer_owner_ = std::move(old_layer_owner);
AddLayerBeneathView(old_layer);
// The old layer is on top and should fade out.
old_layer->SetOpacity(1.f);
new_layer->SetOpacity(1.f);
{
ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
settings.AddObserver(this);
settings.SetTransitionDuration(duration);
old_layer->SetOpacity(0.f);
settings.SetTweenType(gfx::Tween::EASE_OUT);
}
}
// views::Views:
const char* GetClassName() const override { return "FrameAnimatorView"; }
std::unique_ptr<ui::Layer> RecreateLayer() override {
// A layer may be recreated for another animation (maximize/restore).
// Just cancel the animation if that happens during animation.
StopAnimation();
return views::View::RecreateLayer();
}
// ViewObserver::
void OnChildViewReordered(views::View* observed_view,
views::View* child) override {
// Stop animation if the child view order has changed during animation.
StopAnimation();
}
void OnViewBoundsChanged(views::View* observed_view) override {
// Stop animation if the frame size changed during animation.
StopAnimation();
SetBoundsRect(parent_->GetLocalBounds());
}
// ui::ImplicitAnimationObserver overrides:
void OnImplicitAnimationsCompleted() override {
RemoveLayerBeneathView(layer_owner_->root());
layer_owner_ = nullptr;
}
private:
void StopAnimation() {
if (layer_owner_) {
layer_owner_->root()->GetAnimator()->StopAnimating();
layer_owner_ = nullptr;
}
initial_paint_ = false;
}
FrameHeader* frame_header_;
views::View* parent_;
std::unique_ptr<ui::LayerTreeOwner> layer_owner_;
};
void FrameHeader::PaintHeader(gfx::Canvas* canvas) {
painted_ = true;
DoPaintHeader(canvas);
}
......@@ -157,6 +213,18 @@ void FrameHeader::SchedulePaintForTitle() {
}
void FrameHeader::SetPaintAsActive(bool paint_as_active) {
// No need to animate if already active.
const bool already_active = (mode_ == Mode::MODE_ACTIVE);
if (already_active == paint_as_active)
return;
mode_ = paint_as_active ? MODE_ACTIVE : MODE_INACTIVE;
// The frame has no content yet to animatie.
if (painted_)
StartTransitionAnimation(kFrameActivationAnimationDuration);
caption_button_container_->SetPaintAsActive(paint_as_active);
if (back_button_)
back_button_->set_paint_as_active(paint_as_active);
......@@ -198,23 +266,15 @@ void FrameHeader::SetFrameTextOverride(
SchedulePaintForTitle();
}
///////////////////////////////////////////////////////////////////////////////
// gfx::AnimationDelegate overrides:
void FrameHeader::AnimationProgressed(const gfx::Animation* animation) {
view_->SchedulePaintInRect(GetPaintedBounds());
}
///////////////////////////////////////////////////////////////////////////////
// FrameHeader, protected:
FrameHeader::FrameHeader(views::Widget* target_widget, views::View* view)
: views::AnimationDelegateViews(view),
target_widget_(target_widget),
view_(view) {
: target_widget_(target_widget), view_(view) {
DCHECK(target_widget);
DCHECK(view);
UpdateFrameHeaderKey();
frame_animator_ = new FrameAnimatorView(this, view);
}
void FrameHeader::UpdateFrameHeaderKey() {
......@@ -269,6 +329,18 @@ void FrameHeader::SetCaptionButtonContainer(
LayoutHeaderInternal();
}
void FrameHeader::StartTransitionAnimation(base::TimeDelta duration) {
aura::Window* window = target_widget_->GetNativeWindow();
// Don't start another animation if the window is already animating
// such as maximize/restore/unminimize.
if (window->layer()->GetAnimator()->is_animating())
return;
frame_animator_->StartAnimation(duration);
frame_animator_->SchedulePaint();
}
///////////////////////////////////////////////////////////////////////////////
// FrameHeader, private:
......
......@@ -7,11 +7,12 @@
#include "ash/public/cpp/ash_public_export.h"
#include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
#include "base/callback.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/views/window/frame_caption_button.h"
namespace gfx {
......@@ -29,13 +30,13 @@ namespace ash {
class CaptionButtonModel;
// Helper class for managing the window header.
class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
class ASH_PUBLIC_EXPORT FrameHeader {
public:
enum Mode { MODE_ACTIVE, MODE_INACTIVE };
static FrameHeader* Get(views::Widget* widget);
~FrameHeader() override;
virtual ~FrameHeader();
const base::string16& frame_text_override() const {
return frame_text_override_;
......@@ -45,7 +46,7 @@ class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
int GetMinimumHeaderWidth() const;
// Paints the header.
void PaintHeader(gfx::Canvas* canvas, Mode mode);
void PaintHeader(gfx::Canvas* canvas);
// Performs layout for the header.
void LayoutHeader();
......@@ -81,9 +82,6 @@ class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
// regardless of what ShouldShowWindowTitle() returns.
void SetFrameTextOverride(const base::string16& frame_text_override);
// views::AnimationDelegateViews:
void AnimationProgressed(const gfx::Animation* animation) override;
void UpdateFrameHeaderKey();
views::View* view() { return view_; }
......@@ -112,19 +110,20 @@ class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
Mode mode() const { return mode_; }
const gfx::SlideAnimation& activation_animation() {
return activation_animation_;
}
virtual void DoPaintHeader(gfx::Canvas* canvas) = 0;
virtual views::CaptionButtonLayoutSize GetButtonLayoutSize() const = 0;
virtual SkColor GetTitleColor() const = 0;
virtual SkColor GetCurrentFrameColor() const = 0;
// Starts fade transition animation with given duration.
void StartTransitionAnimation(base::TimeDelta duration);
private:
class FrameAnimatorView;
FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, BackButtonAlignment);
FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, TitleIconAlignment);
FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, FrameColors);
friend class FramePaintWaiter;
void LayoutHeaderInternal();
......@@ -139,20 +138,19 @@ class ASH_PUBLIC_EXPORT FrameHeader : public views::AnimationDelegateViews {
views::FrameCaptionButton* back_button_ = nullptr; // May remain nullptr.
views::View* left_header_view_ = nullptr; // May remain nullptr.
FrameCaptionButtonContainerView* caption_button_container_ = nullptr;
FrameAnimatorView* frame_animator_ = nullptr; // owned by view tree.
// The height of the header to paint.
int painted_height_ = 0;
// Used to skip animation when the frame hasn't painted yet.
bool painted_ = false;
// Whether the header should be painted as active.
Mode mode_ = MODE_INACTIVE;
// Whether the header is painted for the first time.
bool initial_paint_ = true;
base::string16 frame_text_override_;
gfx::SlideAnimation activation_animation_{this};
DISALLOW_COPY_AND_ASSIGN(FrameHeader);
};
......
......@@ -34,39 +34,31 @@ void PaintThemedFrame(gfx::Canvas* canvas,
SkColor background_color,
const gfx::Rect& bounds,
int image_inset_x,
int image_inset_y,
int alpha) {
int image_inset_y) {
SkColor opaque_background_color =
SkColorSetA(background_color, SK_AlphaOPAQUE);
// When no images are used, just draw a color, with the animation |alpha|
// applied.
// When no images are used, just draw a color.
if (frame_image.isNull() && frame_overlay_image.isNull()) {
// We use kPlus blending mode so that between the active and inactive
// background colors, the result is 255 alpha (i.e. opaque).
canvas->DrawColor(SkColorSetA(opaque_background_color, alpha),
SkBlendMode::kPlus);
canvas->DrawColor(opaque_background_color);
return;
}
// This handles the case where blending is required between one or more images
// and the background color. In this case we use a SaveLayerWithFlags() call
// to draw all 2-3 components into a single layer then apply the alpha to them
// together.
// to draw all 2-3 components into a single layer.
const bool blending_required =
alpha < 0xFF || (!frame_image.isNull() && !frame_overlay_image.isNull());
!frame_image.isNull() && !frame_overlay_image.isNull();
if (blending_required) {
cc::PaintFlags flags;
// We use kPlus blending mode so that between the active and inactive
// background colors, the result is 255 alpha (i.e. opaque).
flags.setBlendMode(SkBlendMode::kPlus);
flags.setAlpha(alpha);
canvas->SaveLayerWithFlags(flags);
}
// Images can be transparent and we expect the background color to be present
// behind them. Here the |alpha| will be applied to the background color by
// the SaveLayer call, so use |opaque_background_color|.
// behind them.
canvas->DrawColor(opaque_background_color);
if (!frame_image.isNull()) {
canvas->TileImageInt(frame_image, image_inset_x, image_inset_y, 0, 0,
......@@ -89,7 +81,6 @@ void PaintFrameImagesInRoundRect(gfx::Canvas* canvas,
const gfx::Rect& bounds,
int image_inset_x,
int image_inset_y,
int alpha,
int corner_radius) {
const SkScalar sk_corner_radius = SkIntToScalar(corner_radius);
const SkScalar radii[8] = {sk_corner_radius,
......@@ -109,7 +100,7 @@ void PaintFrameImagesInRoundRect(gfx::Canvas* canvas,
canvas->ClipPath(frame_path, antialias);
PaintThemedFrame(canvas, frame_image, frame_overlay_image, background_color,
bounds, image_inset_x, image_inset_y, alpha);
bounds, image_inset_x, image_inset_y);
}
} // namespace
......@@ -147,8 +138,7 @@ int BrowserFrameHeaderAsh::GetThemeBackgroundXInset() {
// BrowserFrameHeaderAsh, protected:
void BrowserFrameHeaderAsh::DoPaintHeader(gfx::Canvas* canvas) {
PaintFrameImages(canvas, false /* active */);
PaintFrameImages(canvas, true /* active */);
PaintFrameImages(canvas);
PaintTitleBar(canvas);
}
......@@ -178,13 +168,8 @@ void BrowserFrameHeaderAsh::UpdateFrameColors() {
///////////////////////////////////////////////////////////////////////////////
// BrowserFrameHeaderAsh, private:
void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas, bool active) {
int alpha = activation_animation().CurrentValueBetween(0, 0xFF);
if (!active)
alpha = 0xFF - alpha;
if (alpha == 0)
return;
void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas) {
const bool active = mode() == Mode::MODE_ACTIVE;
gfx::ImageSkia frame_image =
appearance_provider_->GetFrameHeaderImage(active);
......@@ -201,5 +186,5 @@ void BrowserFrameHeaderAsh::PaintFrameImages(gfx::Canvas* canvas, bool active) {
appearance_provider_->GetFrameHeaderColor(active),
GetPaintedBounds(), GetThemeBackgroundXInset(),
appearance_provider_->GetFrameHeaderImageYInset(),
alpha, corner_radius);
corner_radius);
}
......@@ -49,9 +49,8 @@ class BrowserFrameHeaderAsh : public ash::FrameHeader {
SkColor GetCurrentFrameColor() const override;
private:
// Paints the frame image for the |active| state based on the current value of
// the activation animation.
void PaintFrameImages(gfx::Canvas* canvas, bool active);
// Paints the frame image.
void PaintFrameImages(gfx::Canvas* canvas);
AppearanceProvider* appearance_provider_ = nullptr;
......
......@@ -326,11 +326,8 @@ void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) {
if (!ShouldPaint())
return;
const ash::FrameHeader::Mode header_mode =
ShouldPaintAsActive() ? ash::FrameHeader::MODE_ACTIVE
: ash::FrameHeader::MODE_INACTIVE;
if (frame_header_)
frame_header_->PaintHeader(canvas, header_mode);
frame_header_->PaintHeader(canvas);
}
void BrowserNonClientFrameViewAsh::Layout() {
......
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