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