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