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.
5 #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
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"
23 const int kBackgroundImages
[] = IMAGE_GRID(IDR_OMNIBOX_CONTENT_SETTING_BUBBLE
);
24 const int kStayOpenTimeMS
= 3200; // Time spent with animation fully open.
29 const int ContentSettingImageView::kOpenTimeMS
= 150;
30 const int ContentSettingImageView::kAnimationDurationMS
=
31 (kOpenTimeMS
* 2) + kStayOpenTimeMS
;
33 ContentSettingImageView::ContentSettingImageView(
34 ContentSettingsType content_type
,
35 LocationBarView
* parent
,
36 const gfx::FontList
& font_list
,
38 SkColor parent_background_color
)
40 content_setting_image_model_(
41 ContentSettingImageModel::CreateContentSettingImageModel(
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
);
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
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_
);
81 LocationBarView::InitTouchableLocationBarChildView(this);
83 slide_animator_
.SetSlideDuration(kAnimationDurationMS
);
84 slide_animator_
.SetTweenType(gfx::Tween::LINEAR
);
87 ContentSettingImageView::~ContentSettingImageView() {
89 bubble_widget_
->RemoveObserver(this);
92 void 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
);
97 if (!content_setting_image_model_
->is_visible()) {
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()));
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()))
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();
126 content_settings
->SetBlockageHasBeenIndicated(
127 content_setting_image_model_
->get_content_settings_type());
131 int ContentSettingImageView::GetBubbleOuterPadding(bool by_icon
) {
132 return LocationBarView::GetItemPadding() - LocationBarView::kBubblePadding
+
133 (by_icon
? 0 : LocationBarView::kIconInternalPadding
);
136 void ContentSettingImageView::AnimationEnded(const gfx::Animation
* animation
) {
137 slide_animator_
.Reset();
138 if (!pause_animation_
) {
139 text_label_
->SetVisible(false);
141 parent_
->SchedulePaint();
145 void ContentSettingImageView::AnimationProgressed(
146 const gfx::Animation
* animation
) {
147 if (!pause_animation_
) {
149 parent_
->SchedulePaint();
153 void ContentSettingImageView::AnimationCanceled(
154 const gfx::Animation
* animation
) {
155 AnimationEnded(animation
);
158 gfx::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
;
174 size_fraction
* (text_label_
->GetPreferredSize().width() +
175 GetTotalSpacingWhileAnimating()), 0);
176 size
.SetToMax(background_painter_
->GetMinimumSize());
181 void ContentSettingImageView::Layout() {
182 const int icon_width
= icon_
->GetPreferredSize().width();
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),
192 bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent
& event
) {
193 // We want to show the bubble on mouse release; that is the standard behavior
198 void ContentSettingImageView::OnMouseReleased(const ui::MouseEvent
& event
) {
199 if (HitTestPoint(event
.location()))
203 void ContentSettingImageView::OnGestureEvent(ui::GestureEvent
* event
) {
204 if (event
->type() == ui::ET_GESTURE_TAP
)
206 if ((event
->type() == ui::ET_GESTURE_TAP
) ||
207 (event
->type() == ui::ET_GESTURE_TAP_DOWN
))
211 void ContentSettingImageView::OnPaintBackground(gfx::Canvas
* canvas
) {
212 if (background_showing())
213 background_painter_
->Paint(canvas
, size());
216 void ContentSettingImageView::OnWidgetDestroying(views::Widget
* widget
) {
217 DCHECK_EQ(bubble_widget_
, widget
);
218 bubble_widget_
->RemoveObserver(this);
219 bubble_widget_
= NULL
;
221 if (pause_animation_
) {
222 slide_animator_
.Reset(pause_animation_state_
);
223 pause_animation_
= false;
224 slide_animator_
.Show();
228 int ContentSettingImageView::GetTotalSpacingWhileAnimating() const {
229 return GetBubbleOuterPadding(true) + LocationBarView::GetItemPadding() +
230 GetBubbleOuterPadding(false);
233 void ContentSettingImageView::OnClick() {
234 if (slide_animator_
.is_animating()) {
235 if (!pause_animation_
) {
236 pause_animation_
= true;
237 pause_animation_state_
= slide_animator_
.GetCurrentValue();
239 slide_animator_
.Reset();
242 content::WebContents
* web_contents
= parent_
->GetWebContents();
243 if (web_contents
&& !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();