content_setting_image_view.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/strings/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 "grit/theme_resources.h" 14#include "ui/base/l10n/l10n_util.h" 15#include "ui/base/resource/resource_bundle.h" 16#include "ui/gfx/color_utils.h" 17#include "ui/views/controls/image_view.h" 18#include "ui/views/controls/label.h" 19#include "ui/views/widget/widget.h" 20 21 22namespace { 23const int kBackgroundImages[] = IMAGE_GRID(IDR_OMNIBOX_CONTENT_SETTING_BUBBLE); 24const int kStayOpenTimeMS = 3200; // Time spent with animation fully open. 25} 26 27 28// static 29const int ContentSettingImageView::kOpenTimeMS = 150; 30const int ContentSettingImageView::kAnimationDurationMS = 31 (kOpenTimeMS * 2) + kStayOpenTimeMS; 32 33ContentSettingImageView::ContentSettingImageView( 34 ContentSettingsType content_type, 35 LocationBarView* parent, 36 const gfx::FontList& font_list, 37 SkColor text_color, 38 SkColor parent_background_color) 39 : parent_(parent), 40 content_setting_image_model_( 41 ContentSettingImageModel::CreateContentSettingImageModel( 42 content_type)), 43 background_painter_( 44 views::Painter::CreateImageGridPainter(kBackgroundImages)), 45 icon_(new views::ImageView), 46 text_label_(new views::Label(base::string16(), font_list)), 47 slide_animator_(this), 48 pause_animation_(false), 49 pause_animation_state_(0.0), 50 bubble_widget_(NULL) { 51 icon_->SetHorizontalAlignment(views::ImageView::LEADING); 52 AddChildView(icon_); 53 54 text_label_->SetVisible(false); 55 text_label_->SetEnabledColor(text_color); 56 // Calculate the actual background color for the label. The background images 57 // are painted atop |parent_background_color|. We grab the color of the 58 // middle pixel of the middle image of the background, which we treat as the 59 // representative color of the entire background (reasonable, given the 60 // current appearance of these images). Then we alpha-blend it over the 61 // parent background color to determine the actual color the label text will 62 // sit atop. 63 const SkBitmap& bitmap( 64 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 65 kBackgroundImages[4])->GetRepresentation(1.0f).sk_bitmap()); 66 SkAutoLockPixels pixel_lock(bitmap); 67 SkColor background_image_color = 68 bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2); 69 // Tricky bit: We alpha blend an opaque version of |background_image_color| 70 // against |parent_background_color| using the original image grid color's 71 // alpha. This is because AlphaBlend(a, b, 255) always returns |a| unchanged 72 // even if |a| is a color with non-255 alpha. 73 text_label_->SetBackgroundColor( 74 color_utils::AlphaBlend(SkColorSetA(background_image_color, 255), 75 parent_background_color, 76 SkColorGetA(background_image_color))); 77 text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 78 text_label_->SetElideBehavior(views::Label::NO_ELIDE); 79 AddChildView(text_label_); 80 81 LocationBarView::InitTouchableLocationBarChildView(this); 82 83 slide_animator_.SetSlideDuration(kAnimationDurationMS); 84 slide_animator_.SetTweenType(gfx::Tween::LINEAR); 85} 86 87ContentSettingImageView::~ContentSettingImageView() { 88 if (bubble_widget_) 89 bubble_widget_->RemoveObserver(this); 90} 91 92void ContentSettingImageView::Update(content::WebContents* web_contents) { 93 // Note: We explicitly want to call this even if |web_contents| is NULL, so we 94 // get hidden properly while the user is editing the omnibox. 95 content_setting_image_model_->UpdateFromWebContents(web_contents); 96 97 if (!content_setting_image_model_->is_visible()) { 98 SetVisible(false); 99 return; 100 } 101 102 icon_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 103 content_setting_image_model_->get_icon())); 104 icon_->SetTooltipText( 105 base::UTF8ToUTF16(content_setting_image_model_->get_tooltip())); 106 SetVisible(true); 107 108 // If the content blockage should be indicated to the user, start the 109 // animation and record that we indicated the blockage. 110 TabSpecificContentSettings* content_settings = web_contents ? 111 TabSpecificContentSettings::FromWebContents(web_contents) : NULL; 112 if (!content_settings || content_settings->IsBlockageIndicated( 113 content_setting_image_model_->get_content_settings_type())) 114 return; 115 116 // We just ignore this blockage if we're already showing some other string to 117 // the user. If this becomes a problem, we could design some sort of queueing 118 // mechanism to show one after the other, but it doesn't seem important now. 119 int string_id = content_setting_image_model_->explanatory_string_id(); 120 if (string_id && !background_showing()) { 121 text_label_->SetText(l10n_util::GetStringUTF16(string_id)); 122 text_label_->SetVisible(true); 123 slide_animator_.Show(); 124 } 125 126 content_settings->SetBlockageHasBeenIndicated( 127 content_setting_image_model_->get_content_settings_type()); 128} 129 130// static 131int ContentSettingImageView::GetBubbleOuterPadding(bool by_icon) { 132 return LocationBarView::GetItemPadding() - LocationBarView::kBubblePadding + 133 (by_icon ? 0 : LocationBarView::kIconInternalPadding); 134} 135 136void ContentSettingImageView::AnimationEnded(const gfx::Animation* animation) { 137 slide_animator_.Reset(); 138 if (!pause_animation_) { 139 text_label_->SetVisible(false); 140 parent_->Layout(); 141 parent_->SchedulePaint(); 142 } 143} 144 145void ContentSettingImageView::AnimationProgressed( 146 const gfx::Animation* animation) { 147 if (!pause_animation_) { 148 parent_->Layout(); 149 parent_->SchedulePaint(); 150 } 151} 152 153void ContentSettingImageView::AnimationCanceled( 154 const gfx::Animation* animation) { 155 AnimationEnded(animation); 156} 157 158gfx::Size ContentSettingImageView::GetPreferredSize() { 159 // Height will be ignored by the LocationBarView. 160 gfx::Size size(icon_->GetPreferredSize()); 161 if (background_showing()) { 162 double state = slide_animator_.GetCurrentValue(); 163 // The fraction of the animation we'll spend animating the string into view, 164 // which is also the fraction we'll spend animating it closed; total 165 // animation (slide out, show, then slide in) is 1.0. 166 const double kOpenFraction = 167 static_cast<double>(kOpenTimeMS) / kAnimationDurationMS; 168 double size_fraction = 1.0; 169 if (state < kOpenFraction) 170 size_fraction = state / kOpenFraction; 171 if (state > (1.0 - kOpenFraction)) 172 size_fraction = (1.0 - state) / kOpenFraction; 173 size.Enlarge( 174 size_fraction * (text_label_->GetPreferredSize().width() + 175 GetTotalSpacingWhileAnimating()), 0); 176 size.SetToMax(background_painter_->GetMinimumSize()); 177 } 178 return size; 179} 180 181void ContentSettingImageView::Layout() { 182 const int icon_width = icon_->GetPreferredSize().width(); 183 icon_->SetBounds( 184 std::min((width() - icon_width) / 2, GetBubbleOuterPadding(true)), 0, 185 icon_width, height()); 186 text_label_->SetBounds( 187 icon_->bounds().right() + LocationBarView::GetItemPadding(), 0, 188 std::max(width() - GetTotalSpacingWhileAnimating() - icon_width, 0), 189 height()); 190} 191 192bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent& event) { 193 // We want to show the bubble on mouse release; that is the standard behavior 194 // for buttons. 195 return true; 196} 197 198void ContentSettingImageView::OnMouseReleased(const ui::MouseEvent& event) { 199 if (HitTestPoint(event.location())) 200 OnClick(); 201} 202 203void ContentSettingImageView::OnGestureEvent(ui::GestureEvent* event) { 204 if (event->type() == ui::ET_GESTURE_TAP) 205 OnClick(); 206 if ((event->type() == ui::ET_GESTURE_TAP) || 207 (event->type() == ui::ET_GESTURE_TAP_DOWN)) 208 event->SetHandled(); 209} 210 211void ContentSettingImageView::OnPaintBackground(gfx::Canvas* canvas) { 212 if (background_showing()) 213 background_painter_->Paint(canvas, size()); 214} 215 216void ContentSettingImageView::OnWidgetDestroying(views::Widget* widget) { 217 DCHECK_EQ(bubble_widget_, widget); 218 bubble_widget_->RemoveObserver(this); 219 bubble_widget_ = NULL; 220 221 if (pause_animation_) { 222 slide_animator_.Reset(pause_animation_state_); 223 pause_animation_ = false; 224 slide_animator_.Show(); 225 } 226} 227 228int ContentSettingImageView::GetTotalSpacingWhileAnimating() const { 229 return GetBubbleOuterPadding(true) + LocationBarView::GetItemPadding() + 230 GetBubbleOuterPadding(false); 231} 232 233void ContentSettingImageView::OnClick() { 234 if (slide_animator_.is_animating()) { 235 if (!pause_animation_) { 236 pause_animation_ = true; 237 pause_animation_state_ = slide_animator_.GetCurrentValue(); 238 } 239 slide_animator_.Reset(); 240 } 241 242 content::WebContents* web_contents = parent_->GetWebContents(); 243 if (web_contents && !bubble_widget_) { 244 bubble_widget_ = 245 parent_->delegate()->CreateViewsBubble(new ContentSettingBubbleContents( 246 ContentSettingBubbleModel::CreateContentSettingBubbleModel( 247 parent_->delegate()->GetContentSettingBubbleModelDelegate(), 248 web_contents, parent_->profile(), 249 content_setting_image_model_->get_content_settings_type()), 250 this, views::BubbleBorder::TOP_RIGHT)); 251 bubble_widget_->AddObserver(this); 252 bubble_widget_->Show(); 253 } 254} 255