Home | History | Annotate | Download | only in infobars
      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/infobars/infobar.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/logging.h"
     10 #include "build/build_config.h"
     11 #include "chrome/browser/infobars/infobar_container.h"
     12 #include "chrome/browser/infobars/infobar_service.h"
     13 #include "ui/gfx/animation/slide_animation.h"
     14 
     15 InfoBar::InfoBar(scoped_ptr<InfoBarDelegate> delegate)
     16     : owner_(NULL),
     17       delegate_(delegate.Pass()),
     18       container_(NULL),
     19       animation_(this),
     20       arrow_height_(0),
     21       arrow_target_height_(kDefaultArrowTargetHeight),
     22       arrow_half_width_(0),
     23       bar_height_(0),
     24       bar_target_height_(kDefaultBarTargetHeight) {
     25   DCHECK(delegate_ != NULL);
     26   animation_.SetTweenType(gfx::Tween::LINEAR);
     27   delegate_->set_infobar(this);
     28 }
     29 
     30 InfoBar::~InfoBar() {
     31   DCHECK(!owner_);
     32 }
     33 
     34 // static
     35 SkColor InfoBar::GetTopColor(InfoBarDelegate::Type infobar_type) {
     36   static const SkColor kWarningBackgroundColorTop =
     37       SkColorSetRGB(255, 242, 183);  // Yellow
     38   static const SkColor kPageActionBackgroundColorTop =
     39       SkColorSetRGB(237, 237, 237);  // Gray
     40   return (infobar_type == InfoBarDelegate::WARNING_TYPE) ?
     41       kWarningBackgroundColorTop : kPageActionBackgroundColorTop;
     42 }
     43 
     44 // static
     45 SkColor InfoBar::GetBottomColor(InfoBarDelegate::Type infobar_type) {
     46   static const SkColor kWarningBackgroundColorBottom =
     47       SkColorSetRGB(250, 230, 145);  // Yellow
     48   static const SkColor kPageActionBackgroundColorBottom =
     49       SkColorSetRGB(217, 217, 217);  // Gray
     50   return (infobar_type == InfoBarDelegate::WARNING_TYPE) ?
     51       kWarningBackgroundColorBottom : kPageActionBackgroundColorBottom;
     52 }
     53 
     54 void InfoBar::SetOwner(InfoBarService* owner) {
     55   DCHECK(!owner_);
     56   owner_ = owner;
     57   delegate_->StoreActiveEntryUniqueID();
     58   PlatformSpecificSetOwner();
     59 }
     60 
     61 void InfoBar::Show(bool animate) {
     62   PlatformSpecificShow(animate);
     63   if (animate) {
     64     animation_.Show();
     65   } else {
     66     animation_.Reset(1.0);
     67     RecalculateHeights(true);
     68   }
     69 }
     70 
     71 void InfoBar::Hide(bool animate) {
     72   PlatformSpecificHide(animate);
     73   if (animate) {
     74     animation_.Hide();
     75   } else {
     76     animation_.Reset(0.0);
     77     // We want to remove ourselves from the container immediately even if we
     78     // still have an owner, which MaybeDelete() won't do.
     79     DCHECK(container_);
     80     container_->RemoveInfoBar(this);
     81     MaybeDelete();  // Necessary if the infobar was already closing.
     82   }
     83 }
     84 
     85 void InfoBar::SetArrowTargetHeight(int height) {
     86   DCHECK_LE(height, kMaximumArrowTargetHeight);
     87   // Once the closing animation starts, we ignore further requests to change the
     88   // target height.
     89   if ((arrow_target_height_ != height) && !animation_.IsClosing()) {
     90     arrow_target_height_ = height;
     91     RecalculateHeights(false);
     92   }
     93 }
     94 
     95 void InfoBar::CloseSoon() {
     96   owner_ = NULL;
     97   PlatformSpecificOnCloseSoon();
     98   MaybeDelete();
     99 }
    100 
    101 void InfoBar::RemoveSelf() {
    102   if (owner_)
    103     owner_->RemoveInfoBar(this);
    104 }
    105 
    106 void InfoBar::SetBarTargetHeight(int height) {
    107   if (bar_target_height_ != height) {
    108     bar_target_height_ = height;
    109     RecalculateHeights(false);
    110   }
    111 }
    112 
    113 void InfoBar::AnimationProgressed(const gfx::Animation* animation) {
    114   RecalculateHeights(false);
    115 }
    116 
    117 void InfoBar::AnimationEnded(const gfx::Animation* animation) {
    118   // When the animation ends, we must ensure the container is notified even if
    119   // the heights haven't changed, lest it never get an "animation finished"
    120   // notification.  (If the browser doesn't get this notification, it will not
    121   // bother to re-layout the content area for the new infobar size.)
    122   RecalculateHeights(true);
    123   MaybeDelete();
    124 }
    125 
    126 void InfoBar::RecalculateHeights(bool force_notify) {
    127   int old_arrow_height = arrow_height_;
    128   int old_bar_height = bar_height_;
    129 
    130   // Find the desired arrow height/half-width.  The arrow area is
    131   // |arrow_height_| * |arrow_half_width_|.  When the bar is opening or closing,
    132   // scaling each of these with the square root of the animation value causes a
    133   // linear animation of the area, which matches the perception of the animation
    134   // of the bar portion.
    135   double scale_factor = sqrt(animation_.GetCurrentValue());
    136   arrow_height_ = static_cast<int>(arrow_target_height_ * scale_factor);
    137   if (animation_.is_animating()) {
    138     arrow_half_width_ = static_cast<int>(std::min(arrow_target_height_,
    139         kMaximumArrowTargetHalfWidth) * scale_factor);
    140   } else {
    141     // When the infobar is not animating (i.e. fully open), we set the
    142     // half-width to be proportionally the same distance between its default and
    143     // maximum values as the height is between its.
    144     arrow_half_width_ = kDefaultArrowTargetHalfWidth +
    145         ((kMaximumArrowTargetHalfWidth - kDefaultArrowTargetHalfWidth) *
    146          ((arrow_height_ - kDefaultArrowTargetHeight) /
    147           (kMaximumArrowTargetHeight - kDefaultArrowTargetHeight)));
    148   }
    149   // Add pixels for the stroke, if the arrow is to be visible at all.  Without
    150   // this, changing the arrow height from 0 to kSeparatorLineHeight would
    151   // produce no visible effect, because the stroke would paint atop the divider
    152   // line above the infobar.
    153   if (arrow_height_)
    154     arrow_height_ += kSeparatorLineHeight;
    155 
    156   bar_height_ = animation_.CurrentValueBetween(0, bar_target_height_);
    157 
    158   // Don't re-layout if nothing has changed, e.g. because the animation step was
    159   // not large enough to actually change the heights by at least a pixel.
    160   bool heights_differ =
    161       (old_arrow_height != arrow_height_) || (old_bar_height != bar_height_);
    162   if (heights_differ)
    163     PlatformSpecificOnHeightsRecalculated();
    164 
    165   if (container_ && (heights_differ || force_notify))
    166     container_->OnInfoBarStateChanged(animation_.is_animating());
    167 }
    168 
    169 void InfoBar::MaybeDelete() {
    170   if (!owner_ && (animation_.GetCurrentValue() == 0.0)) {
    171     if (container_)
    172       container_->RemoveInfoBar(this);
    173     delete this;
    174   }
    175 }
    176