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