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/frame/contents_container.h"
6
7#include "base/logging.h"
8#include "third_party/skia/include/core/SkColor.h"
9#include "ui/base/animation/slide_animation.h"
10#include "views/background.h"
11#include "views/widget/root_view.h"
12#include "views/widget/widget.h"
13
14// Min/max opacity of the overlay.
15static const int kMinOpacity = 0;
16static const int kMaxOpacity = 192;
17
18// View used to track when the overlay widget is destroyed. If the
19// ContentsContainer is still valid when the destructor is invoked this invokes
20// |OverlayViewDestroyed| on the ContentsContainer.
21class ContentsContainer::OverlayContentView : public views::View {
22 public:
23  explicit OverlayContentView(ContentsContainer* container)
24      : container_(container) {
25  }
26  ~OverlayContentView() {
27    if (container_)
28      container_->OverlayViewDestroyed();
29  }
30
31  void Detach() {
32    container_ = NULL;
33  }
34
35 private:
36  ContentsContainer* container_;
37
38  DISALLOW_COPY_AND_ASSIGN(OverlayContentView);
39};
40
41ContentsContainer::ContentsContainer(views::View* active)
42    : active_(active),
43      preview_(NULL),
44      preview_tab_contents_(NULL),
45      active_overlay_(NULL),
46      overlay_view_(NULL),
47      active_top_margin_(0) {
48  AddChildView(active_);
49}
50
51ContentsContainer::~ContentsContainer() {
52  // We don't need to explicitly delete active_overlay_ as it'll be deleted by
53  // virtue of being a child window.
54  if (overlay_view_)
55    overlay_view_->Detach();
56}
57
58void ContentsContainer::MakePreviewContentsActiveContents() {
59  DCHECK(preview_);
60  RemoveFade();
61
62  active_ = preview_;
63  preview_ = NULL;
64  Layout();
65}
66
67void ContentsContainer::SetPreview(views::View* preview,
68                                   TabContents* preview_tab_contents) {
69  if (preview == preview_)
70    return;
71
72  if (preview_)
73    RemoveChildView(preview_);
74  preview_ = preview;
75  preview_tab_contents_ = preview_tab_contents;
76  if (preview_)
77    AddChildView(preview_);
78
79  Layout();
80}
81
82void ContentsContainer::SetActiveTopMargin(int margin) {
83  if (active_top_margin_ == margin)
84    return;
85
86  active_top_margin_ = margin;
87  // Make sure we layout next time around. We need this in case our bounds
88  // haven't changed.
89  InvalidateLayout();
90}
91
92gfx::Rect ContentsContainer::GetPreviewBounds() {
93  gfx::Point screen_loc;
94  ConvertPointToScreen(this, &screen_loc);
95  return gfx::Rect(screen_loc, size());
96}
97
98void ContentsContainer::FadeActiveContents() {
99  if (active_overlay_ || !ui::Animation::ShouldRenderRichAnimation())
100    return;
101
102#if !defined(OS_WIN)
103  // TODO: fix this. I'm disabling as z-order isn't right on linux so that
104  // overlay ends up obscuring the omnibox.
105  return;
106#endif
107
108  overlay_animation_.reset(new ui::SlideAnimation(this));
109  overlay_animation_->SetDuration(300);
110  overlay_animation_->SetSlideDuration(300);
111  overlay_animation_->Show();
112
113  CreateOverlay(kMinOpacity);
114}
115
116void ContentsContainer::ShowFade() {
117  if (active_overlay_ || !ui::Animation::ShouldRenderRichAnimation())
118    return;
119
120  CreateOverlay(kMaxOpacity);
121}
122
123void ContentsContainer::RemoveFade() {
124  overlay_animation_.reset();
125  if (active_overlay_) {
126    overlay_view_->Detach();
127    overlay_view_ = NULL;
128    active_overlay_->Close();
129    active_overlay_ = NULL;
130  }
131}
132
133void ContentsContainer::AnimationProgressed(const ui::Animation* animation) {
134  active_overlay_->SetOpacity(
135      ui::Tween::ValueBetween(animation->GetCurrentValue(), kMinOpacity,
136                              kMaxOpacity));
137  active_overlay_->GetRootView()->SchedulePaint();
138}
139
140void ContentsContainer::Layout() {
141  // The active view always gets the full bounds.
142  active_->SetBounds(0, active_top_margin_, width(),
143                     std::max(0, height() - active_top_margin_));
144
145  if (preview_)
146    preview_->SetBounds(0, 0, width(), height());
147
148  // Need to invoke views::View in case any views whose bounds didn't change
149  // still need a layout.
150  views::View::Layout();
151}
152
153void ContentsContainer::CreateOverlay(int initial_opacity) {
154  DCHECK(!active_overlay_);
155  views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
156  params.transparent = true;
157  params.accept_events = false;
158  active_overlay_ = views::Widget::CreateWidget(params);
159  active_overlay_->SetOpacity(initial_opacity);
160  gfx::Point screen_origin;
161  views::View::ConvertPointToScreen(active_, &screen_origin);
162  gfx::Rect overlay_bounds(screen_origin, active_->size());
163  active_overlay_->Init(active_->GetWidget()->GetNativeView(), overlay_bounds);
164  overlay_view_ = new OverlayContentView(this);
165  overlay_view_->set_background(
166      views::Background::CreateSolidBackground(SK_ColorWHITE));
167  active_overlay_->SetContentsView(overlay_view_);
168  active_overlay_->Show();
169  active_overlay_->MoveAboveWidget(active_->GetWidget());
170}
171
172void ContentsContainer::OverlayViewDestroyed() {
173  active_overlay_ = NULL;
174  overlay_view_ = NULL;
175}
176