diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 20:02:18 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 20:02:18 +0000 |
commit | d40c8492826ae9ac77b12c2cff7fbceb2075a772 (patch) | |
tree | a4ec160749f7f644654e9cb9165e8a1283b4574a | |
parent | 379f172c0f5573f49b03a72a22f917f3c2d3caae (diff) | |
download | chromium_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.gyp | 1 | ||||
-rw-r--r-- | app/app_base.gypi | 2 | ||||
-rw-r--r-- | app/multi_animation.cc | 59 | ||||
-rw-r--r-- | app/multi_animation.h | 63 | ||||
-rw-r--r-- | app/multi_animation_unittest.cc | 36 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_renderer.cc | 116 | ||||
-rw-r--r-- | chrome/browser/views/tabs/tab_renderer.h | 4 |
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, ¤t_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 |