summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-05 20:02:18 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-05 20:02:18 +0000
commitd40c8492826ae9ac77b12c2cff7fbceb2075a772 (patch)
treea4ec160749f7f644654e9cb9165e8a1283b4574a
parent379f172c0f5573f49b03a72a22f917f3c2d3caae (diff)
downloadchromium_src-d40c8492826ae9ac77b12c2cff7fbceb2075a772.zip
chromium_src-d40c8492826ae9ac77b12c2cff7fbceb2075a772.tar.gz
chromium_src-d40c8492826ae9ac77b12c2cff7fbceb2075a772.tar.bz2
Changes the mini tab title animation.
BUG=none TEST=none Review URL: http://codereview.chromium.org/1902005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46485 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--app/app.gyp1
-rw-r--r--app/app_base.gypi2
-rw-r--r--app/multi_animation.cc59
-rw-r--r--app/multi_animation.h63
-rw-r--r--app/multi_animation_unittest.cc36
-rw-r--r--chrome/browser/views/tabs/tab_renderer.cc116
-rw-r--r--chrome/browser/views/tabs/tab_renderer.h4
7 files changed, 250 insertions, 31 deletions
diff --git a/app/app.gyp b/app/app.gyp
index f03cc0a..6b7b706 100644
--- a/app/app.gyp
+++ b/app/app.gyp
@@ -43,6 +43,7 @@
'clipboard/clipboard_unittest.cc',
'l10n_util_mac_unittest.mm',
'l10n_util_unittest.cc',
+ 'multi_animation_unittest.cc',
'os_exchange_data_win_unittest.cc',
'run_all_unittests.cc',
'slide_animation_unittest.cc',
diff --git a/app/app_base.gypi b/app/app_base.gypi
index b28bc2b..6991816 100644
--- a/app/app_base.gypi
+++ b/app/app_base.gypi
@@ -155,6 +155,8 @@
'menus/simple_menu_model.cc',
'menus/simple_menu_model.h',
'message_box_flags.h',
+ 'multi_animation.cc',
+ 'multi_animation.h',
'os_exchange_data_provider_gtk.cc',
'os_exchange_data_provider_gtk.h',
'os_exchange_data_provider_win.cc',
diff --git a/app/multi_animation.cc b/app/multi_animation.cc
new file mode 100644
index 0000000..703ed85
--- /dev/null
+++ b/app/multi_animation.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2010 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 "app/multi_animation.h"
+
+// Default interval, in ms.
+static const int kDefaultInterval = 20;
+
+static int TotalTime(const MultiAnimation::Parts& parts) {
+ int time_ms = 0;
+ for (size_t i = 0; i < parts.size(); ++i)
+ time_ms += parts[i].time_ms;
+ return time_ms;
+}
+
+MultiAnimation::MultiAnimation(const Parts& parts)
+ : Animation(base::TimeDelta::FromMilliseconds(kDefaultInterval)),
+ parts_(parts),
+ cycle_time_ms_(TotalTime(parts)),
+ current_value_(0),
+ current_part_index_(0) {
+ DCHECK(!parts_.empty());
+}
+
+void MultiAnimation::Step(base::TimeTicks time_now) {
+ double last_value = current_value_;
+ size_t last_index = current_part_index_;
+
+ int delta = static_cast<int>((time_now - start_time()).InMilliseconds() %
+ cycle_time_ms_);
+ const Part& part = GetPart(&delta, &current_part_index_);
+ double percent = static_cast<double>(delta) /
+ static_cast<double>(part.time_ms);
+ current_value_ = Tween::CalculateValue(part.type, percent);
+
+ if ((current_value_ != last_value || current_part_index_ != last_index) &&
+ delegate()) {
+ delegate()->AnimationProgressed(this);
+ }
+}
+
+const MultiAnimation::Part& MultiAnimation::GetPart(int* time_ms,
+ size_t* part_index) {
+ DCHECK(*time_ms < cycle_time_ms_);
+
+ for (size_t i = 0; i < parts_.size(); ++i) {
+ if (*time_ms < parts_[i].time_ms) {
+ *part_index = i;
+ return parts_[i];
+ }
+
+ *time_ms -= parts_[i].time_ms;
+ }
+ NOTREACHED();
+ *time_ms = 0;
+ *part_index = 0;
+ return parts_[0];
+}
diff --git a/app/multi_animation.h b/app/multi_animation.h
new file mode 100644
index 0000000..ac0ab84
--- /dev/null
+++ b/app/multi_animation.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 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 APP_MULTI_ANIMATION_H_
+#define APP_MULTI_ANIMATION_H_
+
+#include <vector>
+
+#include "app/animation.h"
+#include "app/tween.h"
+
+// MultiAnimation is an animation that consists of a number of sub animations.
+// To create a MultiAnimation pass in the parts, invoke Start() and the delegate
+// is notified as the animation progresses. MultiAnimation runs until Stop is
+// invoked.
+class MultiAnimation : public Animation {
+ public:
+ struct Part {
+ Part() : time_ms(0), type(Tween::ZERO) {}
+ Part(int time_ms, Tween::Type type) : time_ms(time_ms), type(type) {}
+
+ int time_ms;
+ Tween::Type type;
+ };
+
+ typedef std::vector<Part> Parts;
+
+ explicit MultiAnimation(const Parts& parts);
+
+ // Returns the current value. The current value for a MultiAnimation is
+ // determined from the tween type of the current part.
+ virtual double GetCurrentValue() const { return current_value_; }
+
+ // Returns the index of the current part.
+ size_t current_part_index() const { return current_part_index_; }
+
+ protected:
+ // Animation overrides.
+ virtual void Step(base::TimeTicks time_now);
+
+ private:
+ // Returns the part containing the specified time. |time_ms| is reset to be
+ // relative to the part containing the time and |part_index| the index of the
+ // part.
+ const Part& GetPart(int* time_ms, size_t* part_index);
+
+ // The parts that make up the animation.
+ const Parts parts_;
+
+ // Total time of all the parts.
+ const int cycle_time_ms_;
+
+ // Current value for the animation.
+ double current_value_;
+
+ // Index of the current part.
+ size_t current_part_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiAnimation);
+};
+
+#endif // APP_MULTI_ANIMATION_H_
diff --git a/app/multi_animation_unittest.cc b/app/multi_animation_unittest.cc
new file mode 100644
index 0000000..c3990591
--- /dev/null
+++ b/app/multi_animation_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2010 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 "app/multi_animation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef testing::Test MultiAnimationTest;
+
+TEST_F(MultiAnimationTest, Basic) {
+ // Create a MultiAnimation with two parts.
+ MultiAnimation::Parts parts;
+ parts.push_back(MultiAnimation::Part(100, Tween::LINEAR));
+ parts.push_back(MultiAnimation::Part(100, Tween::EASE_OUT));
+
+ MultiAnimation animation(parts);
+ AnimationContainer::Element* as_element =
+ static_cast<AnimationContainer::Element*>(&animation);
+ as_element->SetStartTime(base::TimeTicks());
+
+ // Step to 50, which is half way through the first part.
+ as_element->Step(base::TimeTicks() + base::TimeDelta::FromMilliseconds(50));
+ EXPECT_EQ(.5, animation.GetCurrentValue());
+
+ // Step to 120, which is 20% through the second part.
+ as_element->Step(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(120));
+ EXPECT_EQ(Tween::CalculateValue(Tween::EASE_OUT, .2),
+ animation.GetCurrentValue());
+
+ // Step to 320, which is 20% through the second part.
+ as_element->Step(base::TimeTicks() +
+ base::TimeDelta::FromMilliseconds(320));
+ EXPECT_EQ(Tween::CalculateValue(Tween::EASE_OUT, .2),
+ animation.GetCurrentValue());
+}
diff --git a/chrome/browser/views/tabs/tab_renderer.cc b/chrome/browser/views/tabs/tab_renderer.cc
index 13aeff9..bff941d 100644
--- a/chrome/browser/views/tabs/tab_renderer.cc
+++ b/chrome/browser/views/tabs/tab_renderer.cc
@@ -8,6 +8,7 @@
#include "app/animation_container.h"
#include "app/l10n_util.h"
+#include "app/multi_animation.h"
#include "app/resource_bundle.h"
#include "app/slide_animation.h"
#include "app/throb_animation.h"
@@ -25,6 +26,7 @@
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
#include "views/widget/widget.h"
#include "views/window/non_client_view.h"
#include "views/window/window.h"
@@ -93,15 +95,21 @@ TabRenderer::TabImage TabRenderer::tab_inactive = {0};
TabRenderer::TabImage TabRenderer::tab_inactive_nano = {0};
TabRenderer::TabImage TabRenderer::tab_alpha_nano = {0};
-// Max opacity for the mini-tab title change animation.
-const double kMiniTitleChangeThrobOpacity = 0.75;
+// Durations for the various parts of the mini tab title animation.
+static const int kMiniTitleChangeAnimationDuration1MS = 1000;
+static const int kMiniTitleChangeAnimationDuration2MS = 500;
+static const int kMiniTitleChangeAnimationDuration3MS = 800;
-// Duration for when the title of an inactive mini-tab changes.
-const int kMiniTitleChangeThrobDuration = 1000;
+// Offset from the right edge for the start of the mini title change animation.
+static const int kMiniTitleChangeInitialXOffset = 6;
-// When the title of a mini-tab in the background changes the size of the icon
-// animates. This is the max size we let the icon go to.
-static const int kMiniTitleChangeMaxFaviconSize = 22;
+// Radius of the radial gradient used for mini title change animation.
+static const int kMiniTitleChangeGradientRadius = 20;
+
+// Colors of the gradient used during the mini title change animation.
+static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE;
+static const SkColor kMiniTitleChangeGradientColor2 =
+ SkColorSetARGB(0, 255, 255, 255);
namespace {
@@ -399,18 +407,17 @@ void TabRenderer::StopPulse() {
void TabRenderer::StartMiniTabTitleAnimation() {
if (!mini_title_animation_.get()) {
- mini_title_animation_.reset(new ThrobAnimation(this));
- mini_title_animation_->SetThrobDuration(kMiniTitleChangeThrobDuration);
- }
-
- if (!mini_title_animation_->is_animating()) {
- mini_title_animation_->StartThrobbing(2);
- } else if (mini_title_animation_->cycles_remaining() <= 2) {
- // The title changed while we're already animating. Add at most one more
- // cycle. This is done in an attempt to smooth out pages that continuously
- // change the title.
- mini_title_animation_->set_cycles_remaining(
- mini_title_animation_->cycles_remaining() + 2);
+ MultiAnimation::Parts parts;
+ parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS,
+ Tween::EASE_OUT));
+ parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration2MS,
+ Tween::ZERO));
+ parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration3MS,
+ Tween::EASE_IN));
+ mini_title_animation_.reset(new MultiAnimation(parts));
+ mini_title_animation_->SetContainer(container_.get());
+ mini_title_animation_->set_delegate(this);
+ mini_title_animation_->Start();
}
}
@@ -444,17 +451,7 @@ void TabRenderer::PaintIcon(gfx::Canvas* canvas) {
// to using that class to render the favicon).
int x = favicon_bounds_.x();
int y = favicon_bounds_.y() + fav_icon_hiding_offset_;
- // TODO(sky): this isn't right for app tabs, but we're redoing this
- // effect separately.
int size = data_.favicon.width();
- if (mini() && mini_title_animation_.get() &&
- mini_title_animation_->is_animating()) {
- int throb_size = mini_title_animation_->CurrentValueBetween(
- size, kMiniTitleChangeMaxFaviconSize);
- x -= (throb_size - size) / 2;
- y -= (throb_size - size) / 2;
- size = throb_size;
- }
canvas->DrawBitmapInt(data_.favicon, 0, 0,
data_.favicon.width(),
data_.favicon.height(),
@@ -700,7 +697,10 @@ void TabRenderer::PaintTabBackground(gfx::Canvas* canvas) {
if (IsSelected()) {
PaintActiveTabBackground(canvas);
} else {
- PaintInactiveTabBackground(canvas);
+ if (mini_title_animation_.get() && mini_title_animation_->is_animating())
+ PaintInactiveTabBackgroundWithTitleChange(canvas);
+ else
+ PaintInactiveTabBackground(canvas);
double throb_value = GetThrobValue();
if (throb_value > 0) {
@@ -715,6 +715,62 @@ void TabRenderer::PaintTabBackground(gfx::Canvas* canvas) {
}
}
+void TabRenderer::PaintInactiveTabBackgroundWithTitleChange(
+ gfx::Canvas* canvas) {
+ // Render the inactive tab background. We'll use this for clipping.
+ gfx::Canvas background_canvas(width(), height(), false);
+ PaintInactiveTabBackground(&background_canvas);
+
+ SkBitmap background_image = background_canvas.ExtractBitmap();
+
+ // Draw a radial gradient to hover_canvas.
+ gfx::Canvas hover_canvas(width(), height(), false);
+ int radius = kMiniTitleChangeGradientRadius;
+ int x0 = width() + radius - kMiniTitleChangeInitialXOffset;
+ int x1 = radius;
+ int x2 = -radius;
+ int x;
+ if (mini_title_animation_->current_part_index() == 0) {
+ x = mini_title_animation_->CurrentValueBetween(x0, x1);
+ } else if (mini_title_animation_->current_part_index() == 1) {
+ x = x1;
+ } else {
+ x = mini_title_animation_->CurrentValueBetween(x1, x2);
+ }
+ SkPaint paint;
+ SkPoint loc = { SkIntToScalar(x), SkIntToScalar(0) };
+ SkColor colors[2];
+ colors[0] = kMiniTitleChangeGradientColor1;
+ colors[1] = kMiniTitleChangeGradientColor2;
+ SkShader* shader = SkGradientShader::CreateRadial(
+ loc,
+ SkIntToScalar(radius),
+ colors,
+ NULL,
+ 2,
+ SkShader::kClamp_TileMode);
+ paint.setShader(shader);
+ shader->unref();
+ hover_canvas.FillRectInt(x - radius, -radius, radius * 2, radius * 2, paint);
+
+ // Draw the radial gradient clipped to the background into hover_image.
+ SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap(
+ hover_canvas.ExtractBitmap(), background_image);
+
+ // Draw the tab background to the canvas.
+ canvas->DrawBitmapInt(background_image, 0, 0);
+
+ // And then the gradient on top of that.
+ if (mini_title_animation_->current_part_index() == 2) {
+ canvas->saveLayerAlpha(NULL,
+ mini_title_animation_->CurrentValueBetween(255, 0));
+ canvas->DrawBitmapInt(hover_image, 0, 0);
+ canvas->restore();
+ } else {
+ canvas->DrawBitmapInt(hover_image, 0, 0);
+ }
+}
+
void TabRenderer::PaintInactiveTabBackground(gfx::Canvas* canvas) {
bool is_otr = data_.off_the_record;
diff --git a/chrome/browser/views/tabs/tab_renderer.h b/chrome/browser/views/tabs/tab_renderer.h
index 64938d75..d09b977 100644
--- a/chrome/browser/views/tabs/tab_renderer.h
+++ b/chrome/browser/views/tabs/tab_renderer.h
@@ -14,6 +14,7 @@
#include "views/view.h"
class AnimationContainer;
+class MultiAnimation;
class SlideAnimation;
class TabContents;
class ThrobAnimation;
@@ -181,6 +182,7 @@ class TabRenderer : public views::View,
// Paint various portions of the Tab
void PaintTitle(SkColor title_color, gfx::Canvas* canvas);
void PaintTabBackground(gfx::Canvas* canvas);
+ void PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas);
void PaintInactiveTabBackground(gfx::Canvas* canvas);
void PaintActiveTabBackground(gfx::Canvas* canvas);
void PaintLoadingAnimation(gfx::Canvas* canvas);
@@ -224,7 +226,7 @@ class TabRenderer : public views::View,
scoped_ptr<ThrobAnimation> pulse_animation_;
// Animation used when the title of an inactive mini tab changes.
- scoped_ptr<ThrobAnimation> mini_title_animation_;
+ scoped_ptr<MultiAnimation> mini_title_animation_;
// Model data. We store this here so that we don't need to ask the underlying
// model, which is tricky since instances of this object can outlive the