1// Copyright 2013 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 "ash/wm/overview/scoped_window_copy.h"
6
7#include "ash/screen_util.h"
8#include "ash/shell.h"
9#include "ui/aura/client/aura_constants.h"
10#include "ui/aura/client/screen_position_client.h"
11#include "ui/aura/window.h"
12#include "ui/aura/window_event_dispatcher.h"
13#include "ui/compositor/layer_animation_observer.h"
14#include "ui/compositor/layer_tree_owner.h"
15#include "ui/gfx/display.h"
16#include "ui/views/widget/widget.h"
17#include "ui/wm/core/shadow_types.h"
18#include "ui/wm/core/window_util.h"
19
20namespace ash {
21
22namespace {
23
24// Creates a copy of |window| with |recreated_layer| in the |target_root|.
25views::Widget* CreateCopyOfWindow(aura::Window* target_root,
26                                  aura::Window* src_window,
27                                  ui::Layer* recreated_layer) {
28  // Save and remove the transform from the layer to later reapply to both the
29  // source and newly created copy window.
30  gfx::Transform transform = recreated_layer->transform();
31  recreated_layer->SetTransform(gfx::Transform());
32
33  src_window->SetTransform(transform);
34  views::Widget* widget = new views::Widget;
35  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
36  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
37  params.parent = src_window->parent();
38  params.keep_on_top = true;
39  widget->set_focus_on_creation(false);
40  widget->Init(params);
41  widget->SetVisibilityChangedAnimationsEnabled(false);
42  std::string name = src_window->name() + " (Copy)";
43  widget->GetNativeWindow()->SetName(name);
44  ::wm::SetShadowType(widget->GetNativeWindow(),
45                               ::wm::SHADOW_TYPE_RECTANGULAR);
46
47  // Set the bounds in the target root window.
48  gfx::Display target_display =
49      Shell::GetScreen()->GetDisplayNearestWindow(target_root);
50  aura::client::ScreenPositionClient* screen_position_client =
51      aura::client::GetScreenPositionClient(src_window->GetRootWindow());
52  if (screen_position_client && target_display.is_valid()) {
53    screen_position_client->SetBounds(widget->GetNativeWindow(),
54        src_window->GetBoundsInScreen(), target_display);
55  } else {
56    widget->SetBounds(src_window->GetBoundsInScreen());
57  }
58  widget->StackAbove(src_window);
59
60  // Move the |recreated_layer| to the newly created window.
61  recreated_layer->set_delegate(src_window->layer()->delegate());
62  gfx::Rect layer_bounds = recreated_layer->bounds();
63  layer_bounds.set_origin(gfx::Point(0, 0));
64  recreated_layer->SetBounds(layer_bounds);
65  recreated_layer->SetVisible(false);
66  recreated_layer->parent()->Remove(recreated_layer);
67
68  aura::Window* window = widget->GetNativeWindow();
69  recreated_layer->SetVisible(true);
70  window->layer()->Add(recreated_layer);
71  window->layer()->StackAtTop(recreated_layer);
72  window->layer()->SetOpacity(1);
73  window->SetTransform(transform);
74  window->Show();
75  return widget;
76}
77
78}  // namespace
79
80// An observer which closes the widget and deletes the layer after an
81// animation finishes.
82class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver {
83 public:
84  CleanupWidgetAfterAnimationObserver(
85      views::Widget* widget,
86      scoped_ptr<ui::LayerTreeOwner> layer_owner);
87
88  // Takes ownership of the widget. At this point the class will delete itself
89  // and clean up the layer when there are no pending animations.
90  void TakeOwnershipOfWidget();
91
92  // ui::LayerAnimationObserver:
93  virtual void OnLayerAnimationEnded(
94      ui::LayerAnimationSequence* sequence) OVERRIDE;
95  virtual void OnLayerAnimationAborted(
96      ui::LayerAnimationSequence* sequence) OVERRIDE;
97  virtual void OnLayerAnimationScheduled(
98      ui::LayerAnimationSequence* sequence) OVERRIDE;
99
100 private:
101  virtual ~CleanupWidgetAfterAnimationObserver();
102
103  // If the necessary conditions have been satisfied to destruct this
104  // class, deletes itself and cleans up the widget and layer.
105  void MaybeDestruct();
106
107  views::Widget* widget_;
108  scoped_ptr<ui::LayerTreeOwner> layer_owner_;
109  bool owns_widget_;
110  int pending_animations_;
111
112  DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
113};
114
115CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
116        views::Widget* widget,
117        scoped_ptr<ui::LayerTreeOwner> layer_owner)
118    : widget_(widget),
119      layer_owner_(layer_owner.Pass()),
120      owns_widget_(false),
121      pending_animations_(0) {
122  widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this);
123}
124
125void CleanupWidgetAfterAnimationObserver::TakeOwnershipOfWidget() {
126  owns_widget_ = true;
127  MaybeDestruct();
128}
129
130void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
131    ui::LayerAnimationSequence* sequence) {
132  pending_animations_--;
133  MaybeDestruct();
134}
135
136void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
137    ui::LayerAnimationSequence* sequence) {
138  pending_animations_--;
139  MaybeDestruct();
140}
141
142void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
143    ui::LayerAnimationSequence* sequence) {
144  pending_animations_++;
145}
146
147CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
148  widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this);
149  widget_->Close();
150  widget_ = NULL;
151}
152
153void CleanupWidgetAfterAnimationObserver::MaybeDestruct() {
154  if (pending_animations_ || !owns_widget_)
155    return;
156  delete this;
157}
158
159ScopedWindowCopy::ScopedWindowCopy(aura::Window* target_root,
160                                   aura::Window* src_window) {
161  scoped_ptr<ui::LayerTreeOwner> layer_owner =
162      ::wm::RecreateLayers(src_window);
163  widget_ = CreateCopyOfWindow(target_root, src_window, layer_owner->root());
164  cleanup_observer_ =
165      new CleanupWidgetAfterAnimationObserver(widget_, layer_owner.Pass());
166}
167
168ScopedWindowCopy::~ScopedWindowCopy() {
169  // The cleanup observer will delete itself and the window when any pending
170  // animations have completed.
171  cleanup_observer_->TakeOwnershipOfWidget();
172}
173
174aura::Window* ScopedWindowCopy::GetWindow() {
175  return widget_->GetNativeWindow();
176}
177
178}  // namespace ash
179