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 22 namespace ash { 23 24 namespace { 25 26 // The animation settings used for window selector animations. 27 class 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 43 void 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 54 gfx::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 63 void 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 83 aura::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 91 const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 200; 92 93 ScopedTransformOverviewWindow::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 104 ScopedTransformOverviewWindow::~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 127 bool 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 142 gfx::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 153 void ScopedTransformOverviewWindow::RestoreWindow() { 154 if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) == 155 ui::SHOW_STATE_MINIMIZED) { 156 window_->Show(); 157 } 158 } 159 160 void ScopedTransformOverviewWindow::RestoreWindowOnExit() { 161 minimized_ = false; 162 original_transform_ = gfx::Transform(); 163 opacity_ = 1; 164 } 165 166 void ScopedTransformOverviewWindow::OnWindowDestroyed() { 167 window_ = NULL; 168 } 169 170 gfx::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 184 gfx::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 197 void 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 233 void 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 242 void 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 267 void 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