// Copyright (c) 2011 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 "chrome/browser/ui/views/frame/contents_container.h"
#include "base/logging.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/animation/slide_animation.h"
#include "views/background.h"
#include "views/widget/root_view.h"
#include "views/widget/widget.h"
// Min/max opacity of the overlay.
static const int kMinOpacity = 0;
static const int kMaxOpacity = 192;
// View used to track when the overlay widget is destroyed. If the
// ContentsContainer is still valid when the destructor is invoked this invokes
// |OverlayViewDestroyed| on the ContentsContainer.
class ContentsContainer::OverlayContentView : public views::View {
public:
explicit OverlayContentView(ContentsContainer* container)
: container_(container) {
}
~OverlayContentView() {
if (container_)
container_->OverlayViewDestroyed();
}
void Detach() {
container_ = NULL;
}
private:
ContentsContainer* container_;
DISALLOW_COPY_AND_ASSIGN(OverlayContentView);
};
ContentsContainer::ContentsContainer(views::View* active)
: active_(active),
preview_(NULL),
preview_tab_contents_(NULL),
active_overlay_(NULL),
overlay_view_(NULL),
active_top_margin_(0) {
AddChildView(active_);
}
ContentsContainer::~ContentsContainer() {
// We don't need to explicitly delete active_overlay_ as it'll be deleted by
// virtue of being a child window.
if (overlay_view_)
overlay_view_->Detach();
}
void ContentsContainer::MakePreviewContentsActiveContents() {
DCHECK(preview_);
RemoveFade();
active_ = preview_;
preview_ = NULL;
Layout();
}
void ContentsContainer::SetPreview(views::View* preview,
TabContents* preview_tab_contents) {
if (preview == preview_)
return;
if (preview_)
RemoveChildView(preview_);
preview_ = preview;
preview_tab_contents_ = preview_tab_contents;
if (preview_)
AddChildView(preview_);
Layout();
}
void ContentsContainer::SetActiveTopMargin(int margin) {
if (active_top_margin_ == margin)
return;
active_top_margin_ = margin;
// Make sure we layout next time around. We need this in case our bounds
// haven't changed.
InvalidateLayout();
}
gfx::Rect ContentsContainer::GetPreviewBounds() {
gfx::Point screen_loc;
ConvertPointToScreen(this, &screen_loc);
return gfx::Rect(screen_loc, size());
}
void ContentsContainer::FadeActiveContents() {
if (active_overlay_ || !ui::Animation::ShouldRenderRichAnimation())
return;
#if !defined(OS_WIN)
// TODO: fix this. I'm disabling as z-order isn't right on linux so that
// overlay ends up obscuring the omnibox.
return;
#endif
overlay_animation_.reset(new ui::SlideAnimation(this));
overlay_animation_->SetDuration(300);
overlay_animation_->SetSlideDuration(300);
overlay_animation_->Show();
CreateOverlay(kMinOpacity);
}
void ContentsContainer::ShowFade() {
if (active_overlay_ || !ui::Animation::ShouldRenderRichAnimation())
return;
CreateOverlay(kMaxOpacity);
}
void ContentsContainer::RemoveFade() {
overlay_animation_.reset();
if (active_overlay_) {
overlay_view_->Detach();
overlay_view_ = NULL;
active_overlay_->Close();
active_overlay_ = NULL;
}
}
void ContentsContainer::AnimationProgressed(const ui::Animation* animation) {
active_overlay_->SetOpacity(
ui::Tween::ValueBetween(animation->GetCurrentValue(), kMinOpacity,
kMaxOpacity));
active_overlay_->GetRootView()->SchedulePaint();
}
void ContentsContainer::Layout() {
// The active view always gets the full bounds.
active_->SetBounds(0, active_top_margin_, width(),
std::max(0, height() - active_top_margin_));
if (preview_)
preview_->SetBounds(0, 0, width(), height());
// Need to invoke views::View in case any views whose bounds didn't change
// still need a layout.
views::View::Layout();
}
void ContentsContainer::CreateOverlay(int initial_opacity) {
DCHECK(!active_overlay_);
views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
params.transparent = true;
params.accept_events = false;
active_overlay_ = views::Widget::CreateWidget(params);
active_overlay_->SetOpacity(initial_opacity);
gfx::Point screen_origin;
views::View::ConvertPointToScreen(active_, &screen_origin);
gfx::Rect overlay_bounds(screen_origin, active_->size());
active_overlay_->Init(active_->GetWidget()->GetNativeView(), overlay_bounds);
overlay_view_ = new OverlayContentView(this);
overlay_view_->set_background(
views::Background::CreateSolidBackground(SK_ColorWHITE));
active_overlay_->SetContentsView(overlay_view_);
active_overlay_->Show();
active_overlay_->MoveAboveWidget(active_->GetWidget());
}
void ContentsContainer::OverlayViewDestroyed() {
active_overlay_ = NULL;
overlay_view_ = NULL;
}