OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ui/views/sidebar/sidebar_base_tab.h" |
| 6 |
| 7 #include <limits> |
| 8 |
| 9 #include "base/utf_string_conversions.h" |
| 10 #include "chrome/browser/tab_contents/tab_contents.h" |
| 11 #include "chrome/browser/ui/view_ids.h" |
| 12 #include "chrome/browser/ui/views/sidebar/sidebar_tab_controller.h" |
| 13 #include "gfx/canvas_skia.h" |
| 14 #include "gfx/favicon_size.h" |
| 15 #include "grit/app_resources.h" |
| 16 #include "grit/generated_resources.h" |
| 17 #include "grit/theme_resources.h" |
| 18 #include "ui/base/animation/animation_container.h" |
| 19 #include "ui/base/animation/linear_animation.h" |
| 20 #include "ui/base/animation/slide_animation.h" |
| 21 #include "ui/base/animation/throb_animation.h" |
| 22 #include "ui/base/resource/resource_bundle.h" |
| 23 #include "ui/base/theme_provider.h" |
| 24 |
| 25 namespace { |
| 26 |
| 27 // How long the pulse throb takes. |
| 28 const int kPulseDurationMs = 200; |
| 29 |
| 30 // How long the hover state takes. |
| 31 const int kHoverDurationMs = 90; |
| 32 |
| 33 // How opaque to make the hover state (out of 1). |
| 34 const double kHoverOpacity = 0.33; |
| 35 |
| 36 } // namespace |
| 37 |
| 38 // A custom animation subclass to manage the icon crash animation. |
| 39 class SidebarBaseTab::IconCrashAnimation : public ui::LinearAnimation, |
| 40 public ui::AnimationDelegate { |
| 41 public: |
| 42 explicit IconCrashAnimation(SidebarBaseTab* target) |
| 43 : ALLOW_THIS_IN_INITIALIZER_LIST(LinearAnimation(1000, 25, this)), |
| 44 target_(target) { |
| 45 } |
| 46 virtual ~IconCrashAnimation() {} |
| 47 |
| 48 // ui::AnimationDelegate overrides: |
| 49 virtual void AnimateToState(double state) { |
| 50 const double kHidingOffset = 27; |
| 51 |
| 52 if (state < .5) { |
| 53 target_->SetIconHidingOffset( |
| 54 static_cast<int>(floor(kHidingOffset * 2.0 * state))); |
| 55 } else { |
| 56 target_->DisplayCrashedIcon(); |
| 57 target_->SetIconHidingOffset( |
| 58 static_cast<int>( |
| 59 floor(kHidingOffset - ((state - .5) * 2.0 * kHidingOffset)))); |
| 60 } |
| 61 } |
| 62 |
| 63 // ui::AnimationDelegate overrides: |
| 64 virtual void AnimationCanceled(const ui::Animation* animation) { |
| 65 target_->SetIconHidingOffset(0); |
| 66 } |
| 67 |
| 68 private: |
| 69 SidebarBaseTab* const target_; |
| 70 |
| 71 DISALLOW_COPY_AND_ASSIGN(IconCrashAnimation); |
| 72 }; |
| 73 |
| 74 SidebarBaseTab::SidebarBaseTab(SidebarTabController* controller) |
| 75 : controller_(controller), |
| 76 closing_(false), |
| 77 icon_hiding_offset_(0), |
| 78 should_display_crashed_icon_(false), |
| 79 loading_animation_frame_(0) { |
| 80 DCHECK(controller_); |
| 81 |
| 82 SetID(VIEW_ID_SIDEBAR_TAB); |
| 83 } |
| 84 |
| 85 SidebarBaseTab::~SidebarBaseTab() { |
| 86 } |
| 87 |
| 88 // views::View overrides. |
| 89 |
| 90 void SidebarBaseTab::OnMouseEntered(const views::MouseEvent& e) { |
| 91 if (!hover_animation_.get()) { |
| 92 hover_animation_.reset(new ui::SlideAnimation(this)); |
| 93 hover_animation_->SetContainer(animation_container_.get()); |
| 94 hover_animation_->SetSlideDuration(kHoverDurationMs); |
| 95 } |
| 96 hover_animation_->SetTweenType(ui::Tween::EASE_OUT); |
| 97 hover_animation_->Show(); |
| 98 } |
| 99 |
| 100 void SidebarBaseTab::OnMouseExited(const views::MouseEvent& e) { |
| 101 hover_animation_->SetTweenType(ui::Tween::EASE_IN); |
| 102 hover_animation_->Hide(); |
| 103 } |
| 104 |
| 105 bool SidebarBaseTab::OnMousePressed(const views::MouseEvent& event) { |
| 106 if (event.IsOnlyLeftMouseButton()) |
| 107 controller_->SelectTab(this); |
| 108 return true; |
| 109 } |
| 110 |
| 111 bool SidebarBaseTab::GetTooltipText(const gfx::Point& p, |
| 112 std::wstring* tooltip) { |
| 113 if (data_.title.empty()) |
| 114 return false; |
| 115 *tooltip = UTF16ToWide(data_.title); |
| 116 return true; |
| 117 } |
| 118 |
| 119 AccessibilityTypes::Role SidebarBaseTab::GetAccessibleRole() { |
| 120 return AccessibilityTypes::ROLE_PAGETAB; |
| 121 } |
| 122 |
| 123 // SidebarBaseTab, public. |
| 124 |
| 125 void SidebarBaseTab::SetData(const SidebarTabRendererData& data) { |
| 126 SidebarTabRendererData old(data_); |
| 127 data_ = data; |
| 128 |
| 129 bool is_performing_crash_animation = |
| 130 crash_animation_.get() && crash_animation_->is_animating(); |
| 131 if (data_.crashed) { |
| 132 if (!should_display_crashed_icon_ && !is_performing_crash_animation) |
| 133 StartCrashAnimation(); |
| 134 } else { |
| 135 if (is_performing_crash_animation) |
| 136 StopCrashAnimation(); |
| 137 ResetCrashedIcon(); |
| 138 } |
| 139 |
| 140 // Sets the accessible name for the tab. |
| 141 SetAccessibleName(data_.title); |
| 142 |
| 143 DCHECK(data_.icon.isNull() || |
| 144 (data_.icon.width() == kFavIconSize && |
| 145 data_.icon.height() == kFavIconSize)); |
| 146 |
| 147 DataChanged(old); |
| 148 |
| 149 Layout(); |
| 150 } |
| 151 |
| 152 bool SidebarBaseTab::UpdateLoadingAnimation( |
| 153 SidebarTabRendererData::NetworkState state) { |
| 154 if (state == data_.network_state && |
| 155 state == SidebarTabRendererData::NETWORK_STATE_NONE) { |
| 156 // If the network state is none and hasn't changed, do nothing. Otherwise |
| 157 // we need to advance the animation frame. |
| 158 return false; |
| 159 } |
| 160 |
| 161 SidebarTabRendererData::NetworkState old_state = data_.network_state; |
| 162 data_.network_state = state; |
| 163 AdvanceLoadingAnimation(old_state, state); |
| 164 return true; |
| 165 } |
| 166 |
| 167 void SidebarBaseTab::StartPulse() { |
| 168 if (!pulse_animation_.get()) { |
| 169 pulse_animation_.reset(new ui::ThrobAnimation(this)); |
| 170 pulse_animation_->SetSlideDuration(kPulseDurationMs); |
| 171 if (animation_container_.get()) |
| 172 pulse_animation_->SetContainer(animation_container_.get()); |
| 173 } |
| 174 pulse_animation_->Reset(); |
| 175 pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); |
| 176 } |
| 177 |
| 178 void SidebarBaseTab::StopPulse() { |
| 179 if (!pulse_animation_.get()) |
| 180 return; |
| 181 |
| 182 pulse_animation_->Stop(); // Do stop so we get notified. |
| 183 pulse_animation_.reset(NULL); |
| 184 } |
| 185 |
| 186 bool SidebarBaseTab::IsSelected() const { |
| 187 return controller_->IsTabSelected(this); |
| 188 } |
| 189 |
| 190 bool SidebarBaseTab::IsExpanded() const { |
| 191 return controller_->IsSidebarExpanded(); |
| 192 } |
| 193 |
| 194 // SidebarBaseTab, protected. |
| 195 |
| 196 void SidebarBaseTab::AdvanceLoadingAnimation( |
| 197 SidebarTabRendererData::NetworkState old_state, |
| 198 SidebarTabRendererData::NetworkState state) { |
| 199 static bool initialized = false; |
| 200 static int loading_animation_frame_count = 0; |
| 201 static int waiting_animation_frame_count = 0; |
| 202 static int waiting_to_loading_frame_count_ratio = 0; |
| 203 if (!initialized) { |
| 204 initialized = true; |
| 205 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 206 SkBitmap loading_animation(*rb.GetBitmapNamed(IDR_THROBBER)); |
| 207 loading_animation_frame_count = |
| 208 loading_animation.width() / loading_animation.height(); |
| 209 SkBitmap waiting_animation(*rb.GetBitmapNamed(IDR_THROBBER_WAITING)); |
| 210 waiting_animation_frame_count = |
| 211 waiting_animation.width() / waiting_animation.height(); |
| 212 waiting_to_loading_frame_count_ratio = |
| 213 waiting_animation_frame_count / loading_animation_frame_count; |
| 214 } |
| 215 |
| 216 // The waiting animation is the reverse of the loading animation, but at a |
| 217 // different rate - the following reverses and scales the animation_frame_ |
| 218 // so that the frame is at an equivalent position when going from one |
| 219 // animation to the other. |
| 220 if (state != old_state) { |
| 221 loading_animation_frame_ = loading_animation_frame_count - |
| 222 (loading_animation_frame_ / waiting_to_loading_frame_count_ratio); |
| 223 } |
| 224 |
| 225 if (state != SidebarTabRendererData::NETWORK_STATE_NONE) { |
| 226 loading_animation_frame_ = (loading_animation_frame_ + 1) % |
| 227 ((state == SidebarTabRendererData::NETWORK_STATE_WAITING) ? |
| 228 waiting_animation_frame_count : loading_animation_frame_count); |
| 229 } else { |
| 230 loading_animation_frame_ = 0; |
| 231 } |
| 232 SchedulePaint(); |
| 233 } |
| 234 |
| 235 double SidebarBaseTab::GetThrobValue() { |
| 236 if (pulse_animation_.get() && pulse_animation_->is_animating()) |
| 237 return pulse_animation_->GetCurrentValue() * kHoverOpacity; |
| 238 |
| 239 return hover_animation_.get() ? |
| 240 kHoverOpacity * hover_animation_->GetCurrentValue() : 0; |
| 241 } |
| 242 |
| 243 void SidebarBaseTab::PaintIcon(gfx::Canvas* canvas, int x, int y, int alpha) { |
| 244 SkPaint paint; |
| 245 paint.setAlpha(alpha); |
| 246 if (data().network_state != SidebarTabRendererData::NETWORK_STATE_NONE) { |
| 247 ThemeProvider* tp = GetThemeProvider(); |
| 248 SkBitmap frames(*tp->GetBitmapNamed( |
| 249 data().network_state == SidebarTabRendererData::NETWORK_STATE_WAITING ? |
| 250 IDR_THROBBER_WAITING : IDR_THROBBER)); |
| 251 int image_size = frames.height(); |
| 252 int image_offset = loading_animation_frame_ * image_size; |
| 253 int dst_y = (height() - image_size) / 2; |
| 254 canvas->DrawBitmapInt(frames, image_offset, 0, image_size, |
| 255 image_size, x, dst_y, image_size, image_size, |
| 256 false, paint); |
| 257 } else { |
| 258 canvas->Save(); |
| 259 canvas->ClipRectInt(0, 0, width(), height()); |
| 260 if (should_display_crashed_icon_) { |
| 261 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 262 SkBitmap crashed_icon(*rb.GetBitmapNamed(IDR_SAD_FAVICON)); |
| 263 canvas->DrawBitmapInt(crashed_icon, 0, 0, crashed_icon.width(), |
| 264 crashed_icon.height(), x, |
| 265 (height() - crashed_icon.height()) / 2 + icon_hiding_offset_, |
| 266 kFavIconSize, kFavIconSize, true, paint); |
| 267 } else { |
| 268 if (!data().icon.isNull()) { |
| 269 int size = data().icon.width(); |
| 270 canvas->DrawBitmapInt(data().icon, 0, 0, |
| 271 data().icon.width(), |
| 272 data().icon.height(), |
| 273 x, y + icon_hiding_offset_, size, size, |
| 274 true, paint); |
| 275 } |
| 276 } |
| 277 canvas->Restore(); |
| 278 } |
| 279 } |
| 280 |
| 281 // ui::AnimationDelegate overrides. |
| 282 |
| 283 void SidebarBaseTab::AnimationProgressed(const ui::Animation* animation) { |
| 284 SchedulePaint(); |
| 285 } |
| 286 |
| 287 void SidebarBaseTab::AnimationCanceled(const ui::Animation* animation) { |
| 288 SchedulePaint(); |
| 289 } |
| 290 |
| 291 void SidebarBaseTab::AnimationEnded(const ui::Animation* animation) { |
| 292 SchedulePaint(); |
| 293 } |
| 294 |
| 295 // SidebarBaseTab, private. |
| 296 |
| 297 void SidebarBaseTab::SetIconHidingOffset(int offset) { |
| 298 icon_hiding_offset_ = offset; |
| 299 SchedulePaint(); |
| 300 } |
| 301 |
| 302 void SidebarBaseTab::DisplayCrashedIcon() { |
| 303 should_display_crashed_icon_ = true; |
| 304 } |
| 305 |
| 306 void SidebarBaseTab::ResetCrashedIcon() { |
| 307 should_display_crashed_icon_ = false; |
| 308 } |
| 309 |
| 310 void SidebarBaseTab::StartCrashAnimation() { |
| 311 if (!crash_animation_.get()) |
| 312 crash_animation_.reset(new IconCrashAnimation(this)); |
| 313 crash_animation_->Stop(); |
| 314 crash_animation_->Start(); |
| 315 } |
| 316 |
| 317 void SidebarBaseTab::StopCrashAnimation() { |
| 318 if (crash_animation_.get()) |
| 319 crash_animation_->Stop(); |
| 320 } |
| 321 |
OLD | NEW |