blob: 60e58f524b7feeb191c0d8d55f9af9305d397610 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/infobars/core/infobar.h"
#include <cmath>
#include <utility>
#include "base/logging.h"
#include "build/build_config.h"
#include "components/infobars/core/infobar_container.h"
#include "components/infobars/core/infobar_manager.h"
#include "ui/gfx/animation/slide_animation.h"
namespace infobars {
InfoBar::InfoBar(std::unique_ptr<InfoBarDelegate> delegate)
: owner_(nullptr),
delegate_(std::move(delegate)),
container_(nullptr),
animation_(this),
height_(0),
target_height_(0) {
DCHECK(delegate_ != nullptr);
animation_.SetTweenType(gfx::Tween::LINEAR);
if (!gfx::Animation::ShouldRenderRichAnimation() ||
!delegate_->ShouldAnimate()) {
animation_.SetSlideDuration(base::TimeDelta());
}
delegate_->set_infobar(this);
}
InfoBar::~InfoBar() {
DCHECK(!owner_);
}
void InfoBar::SetOwner(InfoBarManager* owner) {
DCHECK(!owner_);
owner_ = owner;
delegate_->set_nav_entry_id(owner->GetActiveEntryID());
PlatformSpecificSetOwner();
}
void InfoBar::Show(bool animate) {
PlatformSpecificShow(animate);
if (animate) {
animation_.Show();
} else {
animation_.Reset(1.0);
RecalculateHeight(true);
}
}
void InfoBar::Hide(bool animate) {
PlatformSpecificHide(animate);
if (animate) {
animation_.Hide();
} else {
animation_.Reset(0.0);
// We want to remove ourselves from the container immediately even if we
// still have an owner, which MaybeDelete() won't do.
DCHECK(container_);
container_->RemoveInfoBar(this);
MaybeDelete(); // Necessary if the infobar was already closing.
}
}
void InfoBar::CloseSoon() {
owner_ = nullptr;
PlatformSpecificOnCloseSoon();
MaybeDelete();
}
void InfoBar::RemoveSelf() {
if (owner_)
owner_->RemoveInfoBar(this);
}
void InfoBar::SetTargetHeight(int height) {
if (target_height_ != height) {
target_height_ = height;
RecalculateHeight(false);
}
}
void InfoBar::AnimationProgressed(const gfx::Animation* animation) {
RecalculateHeight(false);
}
void InfoBar::AnimationEnded(const gfx::Animation* animation) {
// When the animation ends, we must ensure the container is notified even if
// the heights haven't changed, lest it never get an "animation finished"
// notification. (If the browser doesn't get this notification, it will not
// bother to re-layout the content area for the new infobar size.)
RecalculateHeight(true);
MaybeDelete();
}
void InfoBar::RecalculateHeight(bool force_notify) {
// If there's no container delegate, there's no way to compute the new height,
// so return immediately. We don't need to worry that this might leave us
// with bogus sizes, because if we're ever re-added to a container, it will
// call Show(false) while re-adding us, which will compute a correct set of
// sizes.
if (!container_ || !container_->delegate())
return;
int old_height = height_;
height_ = animation_.CurrentValueBetween(0, target_height_);
// Don't re-layout if nothing has changed, e.g. because the animation step was
// not large enough to actually change the height by at least a pixel.
bool height_differs = old_height != height_;
if (height_differs)
PlatformSpecificOnHeightRecalculated();
if (height_differs || force_notify)
container_->OnInfoBarStateChanged(animation_.is_animating());
}
void InfoBar::MaybeDelete() {
if (!owner_ && (animation_.GetCurrentValue() == 0.0)) {
if (container_)
container_->RemoveInfoBar(this);
delete this;
}
}
} // namespace infobars