content_setting_image_view.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 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/location_bar/content_setting_image_view.h" 6 7#include "base/utf_string_conversions.h" 8#include "chrome/browser/content_settings/tab_specific_content_settings.h" 9#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" 10#include "chrome/browser/ui/content_settings/content_setting_image_model.h" 11#include "chrome/browser/ui/views/content_setting_bubble_contents.h" 12#include "chrome/browser/ui/views/location_bar/location_bar_view.h" 13#include "content/public/browser/web_contents.h" 14#include "third_party/skia/include/core/SkShader.h" 15#include "ui/base/animation/slide_animation.h" 16#include "ui/base/animation/tween.h" 17#include "ui/base/l10n/l10n_util.h" 18#include "ui/base/resource/resource_bundle.h" 19#include "ui/gfx/canvas.h" 20#include "ui/gfx/skia_util.h" 21#include "ui/views/border.h" 22#include "ui/views/controls/image_view.h" 23#include "ui/views/controls/label.h" 24#include "ui/views/layout/box_layout.h" 25#include "ui/views/widget/widget.h" 26 27using content::WebContents; 28 29 30namespace { 31// Animation parameters. 32const int kFrameRateHz = 60; 33const int kOpenTimeMs = 150; 34const int kFullOpenedTimeMs = 3200; 35const int kMoveTimeMs = kFullOpenedTimeMs + 2 * kOpenTimeMs; 36 37// The fraction of the animation we'll spend animating the string into view, and 38// then again animating it closed - total animation (slide out, show, then 39// slide in) is 1.0. 40const double kAnimatingFraction = kOpenTimeMs * 1.0 / kMoveTimeMs; 41 42// Margins for animated box (pixels). 43const int kHorizMargin = 4; 44const int kIconLabelSpacing = 4; 45} 46 47 48ContentSettingImageView::ContentSettingImageView( 49 ContentSettingsType content_type, 50 const int background_images[], 51 LocationBarView* parent, 52 const gfx::Font& font, 53 int font_y_offset, 54 SkColor font_color) 55 : parent_(parent), 56 text_label_(NULL), 57 icon_(new views::ImageView), 58 font_(font), 59 font_color_(font_color), 60 pause_animation_(false), 61 pause_animation_state_(0.0), 62 text_size_(0), 63 visible_text_size_(0), 64 force_draw_text_(false), 65 background_painter_(background_images), 66 content_setting_image_model_( 67 ContentSettingImageModel::CreateContentSettingImageModel( 68 content_type)), 69 bubble_widget_(NULL) { 70 SetLayoutManager(new views::BoxLayout( 71 views::BoxLayout::kHorizontal, 0, 0, 0)); 72 icon_->SetHorizontalAlignment(views::ImageView::LEADING); 73 AddChildView(icon_); 74 TouchableLocationBarView::Init(this); 75} 76 77ContentSettingImageView::~ContentSettingImageView() { 78 if (bubble_widget_) { 79 bubble_widget_->RemoveObserver(this); 80 bubble_widget_ = NULL; 81 } 82} 83 84void ContentSettingImageView::Update(WebContents* web_contents) { 85 if (web_contents) { 86 content_setting_image_model_->UpdateFromWebContents(web_contents); 87 } 88 if (!content_setting_image_model_->is_visible()) { 89 SetVisible(false); 90 return; 91 } 92 SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 93 content_setting_image_model_->get_icon())); 94 SetTooltipText(UTF8ToUTF16(content_setting_image_model_->get_tooltip())); 95 SetVisible(true); 96 97 TabSpecificContentSettings* content_settings = NULL; 98 if (web_contents) { 99 content_settings = 100 TabSpecificContentSettings::FromWebContents(web_contents); 101 } 102 103 if (!content_settings || content_settings->IsBlockageIndicated( 104 content_setting_image_model_->get_content_settings_type())) 105 return; 106 107 // The content blockage was not yet indicated to the user. Start indication 108 // animation and clear "not yet shown" flag. 109 content_settings->SetBlockageHasBeenIndicated( 110 content_setting_image_model_->get_content_settings_type()); 111 112 int animated_string_id = 113 content_setting_image_model_->explanatory_string_id(); 114 // Check if the string for animation is available. 115 if (!animated_string_id) 116 return; 117 118 StartLabelAnimation(l10n_util::GetStringUTF16(animated_string_id), 119 kMoveTimeMs); 120} 121 122void ContentSettingImageView::SetImage(const gfx::ImageSkia* image_skia) { 123 icon_->SetImage(image_skia); 124} 125 126void ContentSettingImageView::SetTooltipText(const string16& tooltip) { 127 icon_->SetTooltipText(tooltip); 128} 129 130gfx::Size ContentSettingImageView::GetPreferredSize() { 131 // Height will be ignored by the LocationBarView. 132 gfx::Size preferred_size(views::View::GetPreferredSize()); 133 int non_label_width = preferred_size.width() - 134 (text_label_ ? text_label_->GetPreferredSize().width() : 0); 135 // When view is animated |visible_text_size_| > 0, it is 0 otherwise. 136 preferred_size.set_width(non_label_width + visible_text_size_); 137 return preferred_size; 138} 139 140void ContentSettingImageView::OnGestureEvent(ui::GestureEvent* event) { 141 if (event->type() == ui::ET_GESTURE_TAP) { 142 AnimationOnClick(); 143 OnClick(parent_); 144 event->SetHandled(); 145 } else if (event->type() == ui::ET_GESTURE_TAP_DOWN) { 146 event->SetHandled(); 147 } 148} 149 150void ContentSettingImageView::AnimationEnded(const ui::Animation* animation) { 151 if (!pause_animation_ && !force_draw_text_) { 152 SetLayoutManager(new views::BoxLayout( 153 views::BoxLayout::kHorizontal, 0, 0, 0)); 154 RemoveChildView(text_label_); // will also delete the view. 155 text_label_ = NULL; 156 parent_->Layout(); 157 parent_->SchedulePaint(); 158 } 159 slide_animator_->Reset(); 160} 161 162void ContentSettingImageView::AnimationProgressed( 163 const ui::Animation* animation) { 164 if (pause_animation_) 165 return; 166 167 visible_text_size_ = GetTextAnimationSize(slide_animator_->GetCurrentValue(), 168 text_size_); 169 170 parent_->Layout(); 171 parent_->SchedulePaint(); 172} 173 174void ContentSettingImageView::AnimationCanceled( 175 const ui::Animation* animation) { 176 AnimationEnded(animation); 177} 178 179int ContentSettingImageView::GetBuiltInHorizontalPadding() const { 180 return GetBuiltInHorizontalPaddingImpl(); 181} 182 183void ContentSettingImageView::OnWidgetDestroying(views::Widget* widget) { 184 if (bubble_widget_) { 185 bubble_widget_->RemoveObserver(this); 186 bubble_widget_ = NULL; 187 } 188 189 UnpauseAnimation(); 190} 191 192void ContentSettingImageView::OnClick(LocationBarView* parent) { 193 WebContents* web_contents = parent->GetWebContents(); 194 if (!web_contents) 195 return; 196 if (bubble_widget_) 197 return; 198 199 Profile* profile = parent->profile(); 200 ContentSettingBubbleContents* bubble = new ContentSettingBubbleContents( 201 ContentSettingBubbleModel::CreateContentSettingBubbleModel( 202 parent->delegate()->GetContentSettingBubbleModelDelegate(), 203 web_contents, 204 profile, 205 content_setting_image_model_->get_content_settings_type()), 206 web_contents, 207 this, 208 views::BubbleBorder::TOP_RIGHT); 209 bubble_widget_ = parent->delegate()->CreateViewsBubble(bubble); 210 bubble_widget_->AddObserver(this); 211 bubble->GetWidget()->Show(); 212} 213 214void ContentSettingImageView::StartLabelAnimation(string16 animated_text, 215 int duration_ms) { 216 if (!slide_animator_.get()) { 217 slide_animator_.reset(new ui::SlideAnimation(this)); 218 slide_animator_->SetSlideDuration(duration_ms); 219 slide_animator_->SetTweenType(ui::Tween::LINEAR); 220 } 221 222 // Do not start animation if already in progress. 223 if (!slide_animator_->is_animating()) { 224 // Initialize animated string. It will be cleared when animation is 225 // completed. 226 if (!text_label_) { 227 text_label_ = new views::Label; 228 text_label_->SetElideBehavior(views::Label::NO_ELIDE); 229 text_label_->SetFont(font_); 230 SetLayoutManager(new views::BoxLayout( 231 views::BoxLayout::kHorizontal, kHorizMargin, 0, kIconLabelSpacing)); 232 AddChildView(text_label_); 233 } 234 text_label_->SetText(animated_text); 235 text_size_ = font_.GetStringWidth(animated_text); 236 text_size_ += kHorizMargin; 237 slide_animator_->Show(); 238 } 239} 240 241int ContentSettingImageView::GetTextAnimationSize(double state, 242 int text_size) { 243 if (state >= 1.0) { 244 // Animaton is over, clear the variables. 245 return 0; 246 } else if (state < kAnimatingFraction) { 247 return static_cast<int>(text_size * state / kAnimatingFraction); 248 } else if (state > (1.0 - kAnimatingFraction)) { 249 return static_cast<int>(text_size * (1.0 - state) / kAnimatingFraction); 250 } else { 251 return text_size; 252 } 253} 254 255void ContentSettingImageView::PauseAnimation() { 256 if (pause_animation_) 257 return; 258 259 pause_animation_ = true; 260 pause_animation_state_ = slide_animator_->GetCurrentValue(); 261} 262 263void ContentSettingImageView::UnpauseAnimation() { 264 if (!pause_animation_) 265 return; 266 267 slide_animator_->Reset(pause_animation_state_); 268 pause_animation_ = false; 269 slide_animator_->Show(); 270} 271 272void ContentSettingImageView::AlwaysDrawText() { 273 force_draw_text_ = true; 274} 275 276bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent& event) { 277 // We want to show the bubble on mouse release; that is the standard behavior 278 // for buttons. 279 return true; 280} 281 282void ContentSettingImageView::OnMouseReleased(const ui::MouseEvent& event) { 283 if (!HitTestPoint(event.location())) 284 return; 285 286 AnimationOnClick(); 287 OnClick(parent_); 288} 289 290void ContentSettingImageView::OnPaintBackground(gfx::Canvas* canvas) { 291 if (force_draw_text_ || (slide_animator_.get() && 292 (slide_animator_->is_animating() || pause_animation_))) 293 background_painter_.Paint(canvas, size()); 294} 295 296void ContentSettingImageView::AnimationOnClick() { 297 // Stop animation. 298 if (slide_animator_.get() && slide_animator_->is_animating()) { 299 PauseAnimation(); 300 slide_animator_->Reset(); 301 } 302} 303