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_transform_overview_window.h"
6
7#include "ash/screen_util.h"
8#include "ash/shell_window_ids.h"
9#include "ash/wm/overview/scoped_window_copy.h"
10#include "ash/wm/overview/window_selector_item.h"
11#include "ash/wm/window_state.h"
12#include "ash/wm/window_util.h"
13#include "ui/aura/client/aura_constants.h"
14#include "ui/aura/client/screen_position_client.h"
15#include "ui/aura/window.h"
16#include "ui/compositor/scoped_layer_animation_settings.h"
17#include "ui/gfx/animation/tween.h"
18#include "ui/views/widget/widget.h"
19#include "ui/wm/core/window_animations.h"
20#include "ui/wm/core/window_util.h"
21
22namespace ash {
23
24namespace {
25
26// The animation settings used for window selector animations.
27class WindowSelectorAnimationSettings
28    : public ui::ScopedLayerAnimationSettings {
29 public:
30  WindowSelectorAnimationSettings(aura::Window* window) :
31      ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) {
32    SetPreemptionStrategy(
33        ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
34    SetTransitionDuration(base::TimeDelta::FromMilliseconds(
35        ScopedTransformOverviewWindow::kTransitionMilliseconds));
36    SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
37  }
38
39  virtual ~WindowSelectorAnimationSettings() {
40  }
41};
42
43void SetTransformOnWindow(aura::Window* window,
44                          const gfx::Transform& transform,
45                          bool animate) {
46  if (animate) {
47    WindowSelectorAnimationSettings animation_settings(window);
48    window->SetTransform(transform);
49  } else {
50    window->SetTransform(transform);
51  }
52}
53
54gfx::Transform TranslateTransformOrigin(const gfx::Vector2d& new_origin,
55                                        const gfx::Transform& transform) {
56  gfx::Transform result;
57  result.Translate(-new_origin.x(), -new_origin.y());
58  result.PreconcatTransform(transform);
59  result.Translate(new_origin.x(), new_origin.y());
60  return result;
61}
62
63void SetTransformOnWindowAndAllTransientChildren(
64    aura::Window* window,
65    const gfx::Transform& transform,
66    bool animate) {
67  SetTransformOnWindow(window, transform, animate);
68
69  aura::Window::Windows transient_children =
70      ::wm::GetTransientChildren(window);
71  for (aura::Window::Windows::iterator iter = transient_children.begin();
72       iter != transient_children.end(); ++iter) {
73    aura::Window* transient_child = *iter;
74    gfx::Rect window_bounds = window->bounds();
75    gfx::Rect child_bounds = transient_child->bounds();
76    gfx::Transform transient_window_transform(
77        TranslateTransformOrigin(child_bounds.origin() - window_bounds.origin(),
78                                 transform));
79    SetTransformOnWindow(transient_child, transient_window_transform, animate);
80  }
81}
82
83aura::Window* GetModalTransientParent(aura::Window* window) {
84  if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW)
85    return ::wm::GetTransientParent(window);
86  return NULL;
87}
88
89}  // namespace
90
91const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 200;
92
93ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(
94        aura::Window* window)
95    : window_(window),
96      minimized_(window->GetProperty(aura::client::kShowStateKey) ==
97                 ui::SHOW_STATE_MINIMIZED),
98      ignored_by_shelf_(ash::wm::GetWindowState(window)->ignored_by_shelf()),
99      overview_started_(false),
100      original_transform_(window->layer()->GetTargetTransform()),
101      opacity_(window->layer()->GetTargetOpacity()) {
102}
103
104ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {
105  if (window_) {
106    WindowSelectorAnimationSettings animation_settings(window_);
107    gfx::Transform transform;
108    SetTransformOnWindowAndTransientChildren(original_transform_, true);
109    if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) !=
110        ui::SHOW_STATE_MINIMIZED) {
111      // Setting opacity 0 and visible false ensures that the property change
112      // to SHOW_STATE_MINIMIZED will not animate the window from its original
113      // bounds to the minimized position.
114      // Hiding the window needs to be done before the target opacity is 0,
115      // otherwise the layer's visibility will not be updated
116      // (See VisibilityController::UpdateLayerVisibility).
117      window_->Hide();
118      window_->layer()->SetOpacity(0);
119      window_->SetProperty(aura::client::kShowStateKey,
120                           ui::SHOW_STATE_MINIMIZED);
121    }
122    ash::wm::GetWindowState(window_)->set_ignored_by_shelf(ignored_by_shelf_);
123    window_->layer()->SetOpacity(opacity_);
124  }
125}
126
127bool ScopedTransformOverviewWindow::Contains(const aura::Window* target) const {
128  for (ScopedVector<ScopedWindowCopy>::const_iterator iter =
129      window_copies_.begin(); iter != window_copies_.end(); ++iter) {
130    if ((*iter)->GetWindow()->Contains(target))
131      return true;
132  }
133  aura::Window* window = window_;
134  while (window) {
135    if (window->Contains(target))
136      return true;
137    window = GetModalTransientParent(window);
138  }
139  return false;
140}
141
142gfx::Rect ScopedTransformOverviewWindow::GetBoundsInScreen() const {
143  gfx::Rect bounds;
144  aura::Window* window = window_;
145  while (window) {
146    bounds.Union(ScreenUtil::ConvertRectToScreen(window->parent(),
147                                                window->GetTargetBounds()));
148    window = GetModalTransientParent(window);
149  }
150  return bounds;
151}
152
153void ScopedTransformOverviewWindow::RestoreWindow() {
154  if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) ==
155      ui::SHOW_STATE_MINIMIZED) {
156    window_->Show();
157  }
158}
159
160void ScopedTransformOverviewWindow::RestoreWindowOnExit() {
161  minimized_ = false;
162  original_transform_ = gfx::Transform();
163  opacity_ = 1;
164}
165
166void ScopedTransformOverviewWindow::OnWindowDestroyed() {
167  window_ = NULL;
168}
169
170gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
171    const gfx::Rect& rect,
172    const gfx::Rect& bounds) {
173  DCHECK(!rect.IsEmpty());
174  DCHECK(!bounds.IsEmpty());
175  float scale = std::min(1.0f,
176      std::min(static_cast<float>(bounds.width()) / rect.width(),
177               static_cast<float>(bounds.height()) / rect.height()));
178  return gfx::Rect(bounds.x() + 0.5 * (bounds.width() - scale * rect.width()),
179                   bounds.y() + 0.5 * (bounds.height() - scale * rect.height()),
180                   rect.width() * scale,
181                   rect.height() * scale);
182}
183
184gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect(
185    const gfx::Rect& src_rect,
186    const gfx::Rect& dst_rect) {
187  DCHECK(!src_rect.IsEmpty());
188  DCHECK(!dst_rect.IsEmpty());
189  gfx::Transform transform;
190  transform.Translate(dst_rect.x() - src_rect.x(),
191                      dst_rect.y() - src_rect.y());
192  transform.Scale(static_cast<float>(dst_rect.width()) / src_rect.width(),
193                  static_cast<float>(dst_rect.height()) / src_rect.height());
194  return transform;
195}
196
197void ScopedTransformOverviewWindow::SetTransform(
198    aura::Window* root_window,
199    const gfx::Transform& transform,
200    bool animate) {
201  DCHECK(overview_started_);
202
203  if (root_window != window_->GetRootWindow()) {
204    if (!window_copies_.empty()) {
205      bool bounds_or_hierarchy_changed = false;
206      aura::Window* window = window_;
207      for (ScopedVector<ScopedWindowCopy>::reverse_iterator iter =
208               window_copies_.rbegin();
209           !bounds_or_hierarchy_changed && iter != window_copies_.rend();
210           ++iter, window = GetModalTransientParent(window)) {
211        if (!window) {
212          bounds_or_hierarchy_changed = true;
213        } else if ((*iter)->GetWindow()->GetBoundsInScreen() !=
214                window->GetBoundsInScreen()) {
215          bounds_or_hierarchy_changed = true;
216        }
217      }
218      // Clearing the window copies array will force it to be recreated.
219      // TODO(flackr): If only the position changed and not the size,
220      // update the existing window copy's position and continue to use it.
221      if (bounds_or_hierarchy_changed)
222        window_copies_.clear();
223    }
224    if (window_copies_.empty()) {
225      // TODO(flackr): Create copies of the transient children windows as well.
226      // Currently they will only be visible on the window's initial display.
227      CopyWindowAndTransientParents(root_window, window_);
228    }
229  }
230  SetTransformOnWindowAndTransientChildren(transform, animate);
231}
232
233void ScopedTransformOverviewWindow::CopyWindowAndTransientParents(
234    aura::Window* target_root,
235    aura::Window* window) {
236  aura::Window* modal_parent = GetModalTransientParent(window);
237  if (modal_parent)
238    CopyWindowAndTransientParents(target_root, modal_parent);
239  window_copies_.push_back(new ScopedWindowCopy(target_root, window));
240}
241
242void ScopedTransformOverviewWindow::SetTransformOnWindowAndTransientChildren(
243    const gfx::Transform& transform,
244    bool animate) {
245  gfx::Point origin(GetBoundsInScreen().origin());
246  aura::Window* window = window_;
247  while (::wm::GetTransientParent(window))
248    window = ::wm::GetTransientParent(window);
249  for (ScopedVector<ScopedWindowCopy>::const_iterator iter =
250      window_copies_.begin(); iter != window_copies_.end(); ++iter) {
251    SetTransformOnWindow(
252        (*iter)->GetWindow(),
253        TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen(
254            (*iter)->GetWindow()->parent(),
255            (*iter)->GetWindow()->GetTargetBounds()).origin() - origin,
256            transform),
257        animate);
258  }
259  SetTransformOnWindowAndAllTransientChildren(
260      window,
261      TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen(
262          window->parent(), window->GetTargetBounds()).origin() - origin,
263          transform),
264      animate);
265}
266
267void ScopedTransformOverviewWindow::PrepareForOverview() {
268  DCHECK(!overview_started_);
269  overview_started_ = true;
270  ash::wm::GetWindowState(window_)->set_ignored_by_shelf(true);
271  RestoreWindow();
272}
273
274}  // namespace ash
275