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 19 namespace ash { 20 21 namespace { 22 23 // The animation settings used for window selector animations. 24 class 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 40 void 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 51 gfx::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 60 void 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 79 aura::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 87 const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 200; 88 89 ScopedTransformOverviewWindow::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 99 ScopedTransformOverviewWindow::~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 121 bool 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 136 gfx::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 147 void ScopedTransformOverviewWindow::RestoreWindow() { 148 if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) == 149 ui::SHOW_STATE_MINIMIZED) { 150 window_->Show(); 151 } 152 } 153 154 void ScopedTransformOverviewWindow::RestoreWindowOnExit() { 155 minimized_ = false; 156 original_transform_ = gfx::Transform(); 157 } 158 159 void ScopedTransformOverviewWindow::OnWindowDestroyed() { 160 window_ = NULL; 161 } 162 163 gfx::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 177 gfx::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 190 void 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 226 void 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 235 void 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 260 void 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