Home | History | Annotate | Download | only in location_bar
      1 // Copyright (c) 2011 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/command_line.h"
      8 #include "base/utf_string_conversions.h"
      9 #include "chrome/browser/content_setting_bubble_model.h"
     10 #include "chrome/browser/content_setting_image_model.h"
     11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     12 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
     13 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
     14 #include "chrome/common/chrome_switches.h"
     15 #include "content/browser/tab_contents/tab_contents.h"
     16 #include "third_party/skia/include/core/SkShader.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/canvas_skia.h"
     21 #include "ui/gfx/skia_util.h"
     22 #include "views/border.h"
     23 
     24 namespace {
     25 // Animation parameters.
     26 const int kOpenTimeMs = 150;
     27 const int kFullOpenedTimeMs = 3200;
     28 const int kMoveTimeMs = kFullOpenedTimeMs + 2 * kOpenTimeMs;
     29 const int kFrameRateHz = 60;
     30 // Colors for the animated box.
     31 const SkColor kTopBoxColor = SkColorSetRGB(0xff, 0xf8, 0xd4);
     32 const SkColor kBottomBoxColor = SkColorSetRGB(0xff, 0xe6, 0xaf);
     33 const SkColor kBorderColor = SkColorSetRGB(0xe9, 0xb9, 0x66);
     34 // Corner radius of the animated box.
     35 const SkScalar kBoxCornerRadius = 2;
     36 // Margins for animated box.
     37 const int kTextMarginPixels = 4;
     38 const int kIconLeftMargin = 4;
     39 
     40 
     41 // The fraction of the animation we'll spend animating the string into view, and
     42 // then again animating it closed -  total animation (slide out, show, then
     43 // slide in) is 1.0.
     44 const double kAnimatingFraction = kOpenTimeMs * 1.0 / kMoveTimeMs;
     45 }
     46 
     47 ContentSettingImageView::ContentSettingImageView(
     48     ContentSettingsType content_type,
     49     LocationBarView* parent,
     50     Profile* profile)
     51     : ui::LinearAnimation(kMoveTimeMs, kFrameRateHz, NULL),
     52       content_setting_image_model_(
     53           ContentSettingImageModel::CreateContentSettingImageModel(
     54               content_type)),
     55       parent_(parent),
     56       profile_(profile),
     57       bubble_(NULL),
     58       animation_in_progress_(false),
     59       text_size_(0),
     60       visible_text_size_(0) {
     61   SetHorizontalAlignment(ImageView::LEADING);
     62 }
     63 
     64 ContentSettingImageView::~ContentSettingImageView() {
     65   if (bubble_)
     66     bubble_->Close();
     67 }
     68 
     69 void ContentSettingImageView::UpdateFromTabContents(TabContents* tab_contents) {
     70   content_setting_image_model_->UpdateFromTabContents(tab_contents);
     71   if (!content_setting_image_model_->is_visible()) {
     72     SetVisible(false);
     73     return;
     74   }
     75   SetImage(ResourceBundle::GetSharedInstance().GetBitmapNamed(
     76       content_setting_image_model_->get_icon()));
     77   SetTooltipText(UTF8ToWide(content_setting_image_model_->get_tooltip()));
     78   SetVisible(true);
     79 
     80   TabSpecificContentSettings* content_settings = tab_contents ?
     81       tab_contents->GetTabSpecificContentSettings() : NULL;
     82   if (!content_settings || content_settings->IsBlockageIndicated(
     83       content_setting_image_model_->get_content_settings_type()))
     84     return;
     85 
     86   // The content blockage was not yet indicated to the user. Start indication
     87   // animation and clear "not yet shown" flag.
     88   content_settings->SetBlockageHasBeenIndicated(
     89       content_setting_image_model_->get_content_settings_type());
     90 
     91   int animated_string_id =
     92       content_setting_image_model_->explanatory_string_id();
     93   // Check if the animation is enabled and if the string for animation is
     94   // available.
     95   if (CommandLine::ForCurrentProcess()->HasSwitch(
     96       switches::kDisableBlockContentAnimation) || !animated_string_id)
     97     return;
     98 
     99   // Do not start animation if already in progress.
    100   if (!animation_in_progress_) {
    101     animation_in_progress_ = true;
    102     // Initialize animated string. It will be cleared when animation is
    103     // completed.
    104     animated_text_ = l10n_util::GetStringUTF16(animated_string_id);
    105     text_size_ = ResourceBundle::GetSharedInstance().GetFont(
    106         ResourceBundle::MediumFont).GetStringWidth(animated_text_);
    107     text_size_ += 2 * kTextMarginPixels + kIconLeftMargin;
    108     if (border())
    109       border()->GetInsets(&saved_insets_);
    110     Start();
    111   }
    112 }
    113 
    114 gfx::Size ContentSettingImageView::GetPreferredSize() {
    115   gfx::Size preferred_size(views::ImageView::GetPreferredSize());
    116   // When view is animated visible_text_size_ > 0, it is 0 otherwise.
    117   preferred_size.set_width(preferred_size.width() + visible_text_size_);
    118   return preferred_size;
    119 }
    120 
    121 bool ContentSettingImageView::OnMousePressed(const views::MouseEvent& event) {
    122   // We want to show the bubble on mouse release; that is the standard behavior
    123   // for buttons.
    124   return true;
    125 }
    126 
    127 void ContentSettingImageView::OnMouseReleased(const views::MouseEvent& event) {
    128   if (!HitTest(event.location()))
    129     return;
    130 
    131   TabContents* tab_contents = parent_->GetTabContentsWrapper()->tab_contents();
    132   if (!tab_contents)
    133     return;
    134 
    135   // Prerender does not have a bubble.
    136   ContentSettingsType content_settings_type =
    137       content_setting_image_model_->get_content_settings_type();
    138   if (content_settings_type == CONTENT_SETTINGS_TYPE_PRERENDER)
    139     return;
    140 
    141   gfx::Rect screen_bounds(GetImageBounds());
    142   gfx::Point origin(screen_bounds.origin());
    143   views::View::ConvertPointToScreen(this, &origin);
    144   screen_bounds.set_origin(origin);
    145   ContentSettingBubbleContents* bubble_contents =
    146       new ContentSettingBubbleContents(
    147           ContentSettingBubbleModel::CreateContentSettingBubbleModel(
    148               tab_contents, profile_, content_settings_type),
    149           profile_, tab_contents);
    150   bubble_ = Bubble::Show(GetWidget(), screen_bounds, BubbleBorder::TOP_RIGHT,
    151                          bubble_contents, this);
    152   bubble_contents->set_bubble(bubble_);
    153 }
    154 
    155 void ContentSettingImageView::VisibilityChanged(View* starting_from,
    156                                                 bool is_visible) {
    157   if (!is_visible && bubble_)
    158     bubble_->Close();
    159 }
    160 
    161 void ContentSettingImageView::OnPaint(gfx::Canvas* canvas) {
    162   gfx::Insets current_insets;
    163   if (border())
    164     border()->GetInsets(&current_insets);
    165   // During the animation we draw a border, an icon and the text. The text area
    166   // is changing in size during the animation, giving the appearance of the text
    167   // sliding out and then back in. When the text completely slid out the yellow
    168   // border is no longer painted around the icon. |visible_text_size_| is 0 when
    169   // animation is stopped.
    170   int necessary_left_margin = std::min(kIconLeftMargin, visible_text_size_);
    171   if (necessary_left_margin != current_insets.left() - saved_insets_.left()) {
    172     // In the non-animated state borders' left() is 0, in the animated state it
    173     // is the kIconLeftMargin, so we need to animate border reduction when it
    174     // starts to disappear.
    175     views::Border* empty_border = views::Border::CreateEmptyBorder(
    176         saved_insets_.top(),
    177         saved_insets_.left() + necessary_left_margin,
    178         saved_insets_.bottom(),
    179         saved_insets_.right());
    180     set_border(empty_border);
    181   }
    182   // Paint an icon with possibly non-empty left border.
    183   views::ImageView::OnPaint(canvas);
    184   if (animation_in_progress_) {
    185     // Paint text to the right of the icon.
    186     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    187     canvas->DrawStringInt(animated_text_,
    188         rb.GetFont(ResourceBundle::MediumFont), SK_ColorBLACK,
    189         GetImageBounds().right() + kTextMarginPixels, y(),
    190         width() - GetImageBounds().width(), height(),
    191         gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::TEXT_VALIGN_MIDDLE);
    192   }
    193 }
    194 
    195 void ContentSettingImageView::OnPaintBackground(gfx::Canvas* canvas) {
    196   if (!animation_in_progress_) {
    197     views::ImageView::OnPaintBackground(canvas);
    198     return;
    199   }
    200   // Paint yellow gradient background if in animation mode.
    201   const int kEdgeThickness = 1;
    202   SkPaint paint;
    203   paint.setShader(gfx::CreateGradientShader(kEdgeThickness,
    204                   height() - (2 * kEdgeThickness),
    205                   kTopBoxColor, kBottomBoxColor));
    206   SkSafeUnref(paint.getShader());
    207   SkRect color_rect;
    208   color_rect.iset(0, 0, width() - 1, height() - 1);
    209   canvas->AsCanvasSkia()->drawRoundRect(color_rect, kBoxCornerRadius,
    210                                         kBoxCornerRadius, paint);
    211   SkPaint outer_paint;
    212   outer_paint.setStyle(SkPaint::kStroke_Style);
    213   outer_paint.setColor(kBorderColor);
    214   color_rect.inset(SkIntToScalar(kEdgeThickness),
    215                    SkIntToScalar(kEdgeThickness));
    216   canvas->AsCanvasSkia()->drawRoundRect(color_rect, kBoxCornerRadius,
    217                                         kBoxCornerRadius, outer_paint);
    218 }
    219 
    220 void ContentSettingImageView::BubbleClosing(Bubble* bubble,
    221                                             bool closed_by_escape) {
    222   bubble_ = NULL;
    223 }
    224 
    225 bool ContentSettingImageView::CloseOnEscape() {
    226   return true;
    227 }
    228 
    229 bool ContentSettingImageView::FadeInOnShow() {
    230   return false;
    231 }
    232 
    233 void ContentSettingImageView::AnimateToState(double state) {
    234   if (state >= 1.0) {
    235     // Animaton is over, clear the variables.
    236     animation_in_progress_ = false;
    237     visible_text_size_ = 0;
    238   } else if (state < kAnimatingFraction) {
    239     visible_text_size_ = static_cast<int>(text_size_ * state /
    240                                           kAnimatingFraction);
    241   } else if (state > (1.0 - kAnimatingFraction)) {
    242     visible_text_size_ = static_cast<int>(text_size_ * (1.0 - state) /
    243                                           kAnimatingFraction);
    244   } else {
    245     visible_text_size_ = text_size_;
    246   }
    247   parent_->Layout();
    248   parent_->SchedulePaint();
    249 }
    250 
    251