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