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