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/infobars/infobar.h" 6 7 #include <cmath> 8 9 #include "ui/base/animation/slide_animation.h" 10 #include "chrome/browser/tab_contents/infobar_delegate.h" 11 #include "chrome/browser/ui/views/infobars/infobar_container.h" 12 13 InfoBar::InfoBar(InfoBarDelegate* delegate) 14 : delegate_(delegate), 15 container_(NULL), 16 ALLOW_THIS_IN_INITIALIZER_LIST(animation_(new ui::SlideAnimation(this))), 17 arrow_height_(0), 18 arrow_target_height_(kDefaultArrowTargetHeight), 19 arrow_half_width_(0), 20 bar_height_(0), 21 bar_target_height_(kDefaultBarTargetHeight) { 22 DCHECK(delegate != NULL); 23 animation_->SetTweenType(ui::Tween::LINEAR); 24 } 25 26 InfoBar::~InfoBar() { 27 } 28 29 void InfoBar::Show(bool animate) { 30 if (animate) { 31 animation_->Show(); 32 } else { 33 animation_->Reset(1.0); 34 AnimationEnded(NULL); 35 } 36 } 37 38 void InfoBar::Hide(bool animate) { 39 PlatformSpecificHide(animate); 40 if (animate) { 41 animation_->Hide(); 42 } else { 43 animation_->Reset(0.0); 44 AnimationEnded(NULL); 45 } 46 } 47 48 void InfoBar::SetArrowTargetHeight(int height) { 49 DCHECK_LE(height, kMaximumArrowTargetHeight); 50 // Once the closing animation starts, we ignore further requests to change the 51 // target height. 52 if ((arrow_target_height_ != height) && !animation()->IsClosing()) { 53 arrow_target_height_ = height; 54 RecalculateHeights(false); 55 } 56 } 57 58 void InfoBar::AnimationProgressed(const ui::Animation* animation) { 59 RecalculateHeights(false); 60 } 61 62 void InfoBar::RemoveInfoBar() { 63 if (container_) 64 container_->RemoveDelegate(delegate_); 65 } 66 67 void InfoBar::SetBarTargetHeight(int height) { 68 if (bar_target_height_ != height) { 69 bar_target_height_ = height; 70 RecalculateHeights(false); 71 } 72 } 73 74 int InfoBar::OffsetY(const gfx::Size& prefsize) const { 75 return arrow_height_ + 76 std::max((bar_target_height_ - prefsize.height()) / 2, 0) - 77 (bar_target_height_ - bar_height_); 78 } 79 80 void InfoBar::AnimationEnded(const ui::Animation* animation) { 81 // When the animation ends, we must ensure the container is notified even if 82 // the heights haven't changed, lest it never get an "animation finished" 83 // notification. (If the browser doesn't get this notification, it will not 84 // bother to re-layout the content area for the new infobar size.) 85 RecalculateHeights(true); 86 MaybeDelete(); 87 } 88 89 void InfoBar::RecalculateHeights(bool force_notify) { 90 int old_arrow_height = arrow_height_; 91 int old_bar_height = bar_height_; 92 93 // Find the desired arrow height/half-width. The arrow area is 94 // |arrow_height_| * |arrow_half_width_|. When the bar is opening or closing, 95 // scaling each of these with the square root of the animation value causes a 96 // linear animation of the area, which matches the perception of the animation 97 // of the bar portion. 98 double scale_factor = sqrt(animation()->GetCurrentValue()); 99 arrow_height_ = static_cast<int>(arrow_target_height_ * scale_factor); 100 if (animation_->is_animating()) { 101 arrow_half_width_ = static_cast<int>(std::min(arrow_target_height_, 102 kMaximumArrowTargetHalfWidth) * scale_factor); 103 } else { 104 // When the infobar is not animating (i.e. fully open), we set the 105 // half-width to be proportionally the same distance between its default and 106 // maximum values as the height is between its. 107 arrow_half_width_ = kDefaultArrowTargetHalfWidth + 108 ((kMaximumArrowTargetHalfWidth - kDefaultArrowTargetHalfWidth) * 109 ((arrow_height_ - kDefaultArrowTargetHeight) / 110 (kMaximumArrowTargetHeight - kDefaultArrowTargetHeight))); 111 } 112 // Add pixels for the stroke, if the arrow is to be visible at all. Without 113 // this, changing the arrow height from 0 to kSeparatorLineHeight would 114 // produce no visible effect, because the stroke would paint atop the divider 115 // line above the infobar. 116 if (arrow_height_) 117 arrow_height_ += kSeparatorLineHeight; 118 119 bar_height_ = 120 static_cast<int>(bar_target_height_ * animation()->GetCurrentValue()); 121 122 // Don't re-layout if nothing has changed, e.g. because the animation step was 123 // not large enough to actually change the heights by at least a pixel. 124 bool heights_differ = 125 (old_arrow_height != arrow_height_) || (old_bar_height != bar_height_); 126 if (heights_differ) 127 PlatformSpecificOnHeightsRecalculated(); 128 129 if (container_ && (heights_differ || force_notify)) 130 container_->OnInfoBarStateChanged(animation_->is_animating()); 131 } 132 133 void InfoBar::MaybeDelete() { 134 if (delegate_ && (animation_->GetCurrentValue() == 0.0)) { 135 if (container_) 136 container_->RemoveInfoBar(this); 137 // Note that we only tell the delegate we're closed here, and not when we're 138 // simply destroyed (by virtue of a tab switch or being moved from window to 139 // window), since this action can cause the delegate to destroy itself. 140 delegate_->InfoBarClosed(); 141 delegate_ = NULL; 142 } 143 } 144