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 slide_animator_
.SetSlideDuration(kAnimationDurationMS
);
82 slide_animator_
.SetTweenType(gfx::Tween::LINEAR
);
85 ContentSettingImageView::~ContentSettingImageView() {
87 bubble_widget_
->RemoveObserver(this);
90 void ContentSettingImageView::Update(content::WebContents
* web_contents
) {
91 // Note: We explicitly want to call this even if |web_contents| is NULL, so we
92 // get hidden properly while the user is editing the omnibox.
93 content_setting_image_model_
->UpdateFromWebContents(web_contents
);
95 if (!content_setting_image_model_
->is_visible()) {
100 icon_
->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
101 content_setting_image_model_
->get_icon()));
102 icon_
->SetTooltipText(
103 base::UTF8ToUTF16(content_setting_image_model_
->get_tooltip()));
106 // If the content blockage should be indicated to the user, start the
107 // animation and record that we indicated the blockage.
108 TabSpecificContentSettings
* content_settings
= web_contents
?
109 TabSpecificContentSettings::FromWebContents(web_contents
) : NULL
;
110 if (!content_settings
|| content_settings
->IsBlockageIndicated(
111 content_setting_image_model_
->get_content_settings_type()))
114 // We just ignore this blockage if we're already showing some other string to
115 // the user. If this becomes a problem, we could design some sort of queueing
116 // mechanism to show one after the other, but it doesn't seem important now.
117 int string_id
= content_setting_image_model_
->explanatory_string_id();
118 if (string_id
&& !background_showing()) {
119 text_label_
->SetText(l10n_util::GetStringUTF16(string_id
));
120 text_label_
->SetVisible(true);
121 slide_animator_
.Show();
124 content_settings
->SetBlockageHasBeenIndicated(
125 content_setting_image_model_
->get_content_settings_type());
129 int ContentSettingImageView::GetBubbleOuterPadding(bool by_icon
) {
130 return LocationBarView::kItemPadding
- LocationBarView::kBubblePadding
+
131 (by_icon
? 0 : LocationBarView::kIconInternalPadding
);
134 void ContentSettingImageView::AnimationEnded(const gfx::Animation
* animation
) {
135 slide_animator_
.Reset();
136 if (!pause_animation_
) {
137 text_label_
->SetVisible(false);
139 parent_
->SchedulePaint();
143 void ContentSettingImageView::AnimationProgressed(
144 const gfx::Animation
* animation
) {
145 if (!pause_animation_
) {
147 parent_
->SchedulePaint();
151 void ContentSettingImageView::AnimationCanceled(
152 const gfx::Animation
* animation
) {
153 AnimationEnded(animation
);
156 gfx::Size
ContentSettingImageView::GetPreferredSize() const {
157 // Height will be ignored by the LocationBarView.
158 gfx::Size
size(icon_
->GetPreferredSize());
159 if (background_showing()) {
160 double state
= slide_animator_
.GetCurrentValue();
161 // The fraction of the animation we'll spend animating the string into view,
162 // which is also the fraction we'll spend animating it closed; total
163 // animation (slide out, show, then slide in) is 1.0.
164 const double kOpenFraction
=
165 static_cast<double>(kOpenTimeMS
) / kAnimationDurationMS
;
166 double size_fraction
= 1.0;
167 if (state
< kOpenFraction
)
168 size_fraction
= state
/ kOpenFraction
;
169 if (state
> (1.0 - kOpenFraction
))
170 size_fraction
= (1.0 - state
) / kOpenFraction
;
172 size_fraction
* (text_label_
->GetPreferredSize().width() +
173 GetTotalSpacingWhileAnimating()), 0);
174 size
.SetToMax(background_painter_
->GetMinimumSize());
179 void ContentSettingImageView::Layout() {
180 const int icon_width
= icon_
->GetPreferredSize().width();
182 std::min((width() - icon_width
) / 2, GetBubbleOuterPadding(true)), 0,
183 icon_width
, height());
184 text_label_
->SetBounds(
185 icon_
->bounds().right() + LocationBarView::kItemPadding
, 0,
186 std::max(width() - GetTotalSpacingWhileAnimating() - icon_width
, 0),
190 bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent
& event
) {
191 // We want to show the bubble on mouse release; that is the standard behavior
196 void ContentSettingImageView::OnMouseReleased(const ui::MouseEvent
& event
) {
197 if (HitTestPoint(event
.location()))
201 void ContentSettingImageView::OnGestureEvent(ui::GestureEvent
* event
) {
202 if (event
->type() == ui::ET_GESTURE_TAP
)
204 if ((event
->type() == ui::ET_GESTURE_TAP
) ||
205 (event
->type() == ui::ET_GESTURE_TAP_DOWN
))
209 void ContentSettingImageView::OnPaintBackground(gfx::Canvas
* canvas
) {
210 if (background_showing())
211 background_painter_
->Paint(canvas
, size());
214 void ContentSettingImageView::OnWidgetDestroying(views::Widget
* widget
) {
215 DCHECK_EQ(bubble_widget_
, widget
);
216 bubble_widget_
->RemoveObserver(this);
217 bubble_widget_
= NULL
;
219 if (pause_animation_
) {
220 slide_animator_
.Reset(pause_animation_state_
);
221 pause_animation_
= false;
222 slide_animator_
.Show();
226 int ContentSettingImageView::GetTotalSpacingWhileAnimating() const {
227 return GetBubbleOuterPadding(true) + LocationBarView::kItemPadding
+
228 GetBubbleOuterPadding(false);
231 void ContentSettingImageView::OnClick() {
232 if (slide_animator_
.is_animating()) {
233 if (!pause_animation_
) {
234 pause_animation_
= true;
235 pause_animation_state_
= slide_animator_
.GetCurrentValue();
237 slide_animator_
.Reset();
240 content::WebContents
* web_contents
= parent_
->GetWebContents();
241 if (web_contents
&& !bubble_widget_
) {
243 parent_
->delegate()->CreateViewsBubble(new ContentSettingBubbleContents(
244 ContentSettingBubbleModel::CreateContentSettingBubbleModel(
245 parent_
->delegate()->GetContentSettingBubbleModelDelegate(),
246 web_contents
, parent_
->profile(),
247 content_setting_image_model_
->get_content_settings_type()),
248 this, views::BubbleBorder::TOP_RIGHT
));
249 bubble_widget_
->AddObserver(this);
250 bubble_widget_
->Show();