Skip to content

Commit

Permalink
Animate frame color change
Browse files Browse the repository at this point in the history
ARC++ app may update the color programatically, which
can result in noticable flashing.

This will make the color change transition smooth by
animating the color from currently used color".

TBR=wutao@chromium.org
BUG=b/88534690
TEST=manual. also covered by unit test

Change-Id: Ia15976cb1ec425ddb4e23ec6a253667fa6f774d9
Reviewed-on: https://chromium-review.googlesource.com/1101504
Commit-Queue: Mitsuru Oshima <oshima@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568474}
  • Loading branch information
mitoshima authored and Commit Bot committed Jun 19, 2018
1 parent dbb8df3 commit 0bf8c18
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 16 deletions.
64 changes: 53 additions & 11 deletions ash/frame/default_frame_header.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ using views::Widget;
namespace {

// Color for the window title text.
const SkColor kTitleTextColor = SkColorSetRGB(40, 40, 40);
const SkColor kLightTitleTextColor = SK_ColorWHITE;
constexpr SkColor kTitleTextColor = SkColorSetRGB(40, 40, 40);
constexpr SkColor kLightTitleTextColor = SK_ColorWHITE;
// This is 2x of the slide ainmation duration.
constexpr int kColorUpdateDurationMs = 240;

// Tiles an image into an area, rounding the top corners.
void TileRoundRect(gfx::Canvas* canvas,
Expand All @@ -54,6 +56,35 @@ void TileRoundRect(gfx::Canvas* canvas,

namespace ash {

DefaultFrameHeader::ColorAnimator::ColorAnimator(
gfx::AnimationDelegate* delegate)
: animation_(delegate) {
animation_.SetSlideDuration(kColorUpdateDurationMs);
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_, animation_.CurrentValueBetween(0, 255));
return current_color_;
}

///////////////////////////////////////////////////////////////////////////////
// DefaultFrameHeader, public:

Expand All @@ -62,8 +93,8 @@ DefaultFrameHeader::DefaultFrameHeader(
views::View* header_view,
FrameCaptionButtonContainerView* caption_button_container)
: FrameHeader(target_widget, header_view),
active_frame_color_(kDefaultFrameColor),
inactive_frame_color_(kDefaultFrameColor) {
active_frame_color_(this),
inactive_frame_color_(this) {
DCHECK(caption_button_container);
SetCaptionButtonContainer(caption_button_container);
}
Expand Down Expand Up @@ -93,8 +124,9 @@ void DefaultFrameHeader::DoPaintHeader(gfx::Canvas* canvas) {

cc::PaintFlags flags;
int active_alpha = activation_animation().CurrentValueBetween(0, 255);
flags.setColor(color_utils::AlphaBlend(active_frame_color_,
inactive_frame_color_, active_alpha));
flags.setColor(color_utils::AlphaBlend(
active_frame_color_.GetCurrentColor(),
inactive_frame_color_.GetCurrentColor(), active_alpha));
flags.setAntiAlias(true);
if (width_in_pixels_ > 0) {
canvas->Save();
Expand Down Expand Up @@ -131,12 +163,12 @@ SkColor DefaultFrameHeader::GetTitleColor() const {
void DefaultFrameHeader::SetFrameColorsImpl(SkColor active_frame_color,
SkColor inactive_frame_color) {
bool updated = false;
if (active_frame_color_ != active_frame_color) {
active_frame_color_ = active_frame_color;
if (active_frame_color_.target_color() != active_frame_color) {
active_frame_color_.SetTargetColor(active_frame_color);
updated = true;
}
if (inactive_frame_color_ != inactive_frame_color) {
inactive_frame_color_ = inactive_frame_color;
if (inactive_frame_color_.target_color() != inactive_frame_color) {
inactive_frame_color_.SetTargetColor(inactive_frame_color);
updated = true;
}

Expand All @@ -147,7 +179,17 @@ void DefaultFrameHeader::SetFrameColorsImpl(SkColor active_frame_color,
}

SkColor DefaultFrameHeader::GetCurrentFrameColor() const {
return mode() == MODE_ACTIVE ? active_frame_color_ : inactive_frame_color_;
return mode() == MODE_ACTIVE ? active_frame_color_.target_color()
: inactive_frame_color_.target_color();
}

gfx::SlideAnimation*
DefaultFrameHeader::GetAnimationForActiveFrameColorForTest() {
return active_frame_color_.animation();
}

SkColor DefaultFrameHeader::GetActiveFrameColorForPaintForTest() {
return active_frame_color_.GetCurrentColor();
}

} // namespace ash
39 changes: 35 additions & 4 deletions ash/frame/default_frame_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "ash/ash_export.h"
#include "ash/frame/frame_header.h"
#include "ash/public/cpp/ash_constants.h"
#include "base/compiler_specific.h" // override
#include "base/gtest_prod_util.h"
#include "base/macros.h"
Expand All @@ -27,8 +28,12 @@ class ASH_EXPORT DefaultFrameHeader : public FrameHeader {

void SetThemeColor(SkColor theme_color);

SkColor active_frame_color_for_testing() { return active_frame_color_; }
SkColor inactive_frame_color_for_testing() { return inactive_frame_color_; }
SkColor active_frame_color_for_testing() {
return active_frame_color_.target_color();
}
SkColor inactive_frame_color_for_testing() {
return inactive_frame_color_.target_color();
}

protected:
// FrameHeader:
Expand All @@ -47,8 +52,34 @@ class ASH_EXPORT DefaultFrameHeader : public FrameHeader {
void SetFrameColorsImpl(SkColor active_frame_color,
SkColor inactive_frame_color);

SkColor active_frame_color_;
SkColor inactive_frame_color_;
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_;

int width_in_pixels_ = -1;

Expand Down
45 changes: 44 additions & 1 deletion ash/frame/default_frame_header_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/test/ash_test_base.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"
Expand Down Expand Up @@ -69,7 +71,6 @@ TEST_F(DefaultFrameHeaderTest, FrameColors) {

DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
&container);

// Check frame color is sensitive to mode.
SkColor active = SkColorSetRGB(70, 70, 70);
SkColor inactive = SkColorSetRGB(200, 200, 200);
Expand All @@ -78,6 +79,48 @@ TEST_F(DefaultFrameHeaderTest, FrameColors) {
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;
SkColor new_active = SkColorSetRGB(70, 70, 0);
frame_header.SetFrameColors(new_active, SK_ColorBLACK);

gfx::SlideAnimation* animation =
frame_header.GetAnimationForActiveFrameColorForTest();
gfx::AnimationTestApi test_api(animation);

// animate half way through.
base::TimeTicks now = base::TimeTicks::Now();
test_api.SetStartTime(now);
test_api.Step(now + base::TimeDelta::FromMilliseconds(120));

// GetCurrentFrameColor should return the target color.
EXPECT_EQ(new_active, frame_header.GetCurrentFrameColor());

// 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));

// Now update to the new value which is full blue.
SkColor new_new_active = SkColorSetRGB(70, 70, 255);
frame_header.SetFrameColors(new_new_active, SK_ColorBLACK);

now = base::TimeTicks::Now();
test_api.SetStartTime(now);
test_api.Step(now + base::TimeDelta::FromMilliseconds(20));

// 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 ash

0 comments on commit 0bf8c18

Please sign in to comment.