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 } 102 103 ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() { 104 if (window_) { 105 WindowSelectorAnimationSettings animation_settings(window_); 106 gfx::Transform transform; 107 SetTransformOnWindowAndTransientChildren(original_transform_, true); 108 if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) != 109 ui::SHOW_STATE_MINIMIZED) { 110 // Setting opacity 0 and visible false ensures that the property change 111 // to SHOW_STATE_MINIMIZED will not animate the window from its original 112 // bounds to the minimized position. 113 // Hiding the window needs to be done before the target opacity is 0, 114 // otherwise the layer's visibility will not be updated 115 // (See VisibilityController::UpdateLayerVisibility). 116 window_->Hide(); 117 window_->layer()->SetOpacity(0); 118 window_->SetProperty(aura::client::kShowStateKey, 119 ui::SHOW_STATE_MINIMIZED); 120 } 121 ash::wm::GetWindowState(window_)->set_ignored_by_shelf(ignored_by_shelf_); 122 } 123 } 124 125 bool ScopedTransformOverviewWindow::Contains(const aura::Window* target) const { 126 for (ScopedVector<ScopedWindowCopy>::const_iterator iter = 127 window_copies_.begin(); iter != window_copies_.end(); ++iter) { 128 if ((*iter)->GetWindow()->Contains(target)) 129 return true; 130 } 131 aura::Window* window = window_; 132 while (window) { 133 if (window->Contains(target)) 134 return true; 135 window = GetModalTransientParent(window); 136 } 137 return false; 138 } 139 140 gfx::Rect ScopedTransformOverviewWindow::GetBoundsInScreen() const { 141 gfx::Rect bounds; 142 aura::Window* window = window_; 143 while (window) { 144 bounds.Union(ScreenUtil::ConvertRectToScreen(window->parent(), 145 window->GetTargetBounds())); 146 window = GetModalTransientParent(window); 147 } 148 return bounds; 149 } 150 151 void ScopedTransformOverviewWindow::RestoreWindow() { 152 if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) == 153 ui::SHOW_STATE_MINIMIZED) { 154 window_->Show(); 155 } 156 } 157 158 void ScopedTransformOverviewWindow::RestoreWindowOnExit() { 159 minimized_ = false; 160 original_transform_ = gfx::Transform(); 161 } 162 163 void ScopedTransformOverviewWindow::OnWindowDestroyed() { 164 window_ = NULL; 165 } 166 167 gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio( 168 const gfx::Rect& rect, 169 const gfx::Rect& bounds) { 170 DCHECK(!rect.IsEmpty()); 171 DCHECK(!bounds.IsEmpty()); 172 float scale = std::min(1.0f, 173 std::min(static_cast<float>(bounds.width()) / rect.width(), 174 static_cast<float>(bounds.height()) / rect.height())); 175 return gfx::Rect(bounds.x() + 0.5 * (bounds.width() - scale * rect.width()), 176 bounds.y() + 0.5 * (bounds.height() - scale * rect.height()), 177 rect.width() * scale, 178 rect.height() * scale); 179 } 180 181 gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect( 182 const gfx::Rect& src_rect, 183 const gfx::Rect& dst_rect) { 184 DCHECK(!src_rect.IsEmpty()); 185 DCHECK(!dst_rect.IsEmpty()); 186 gfx::Transform transform; 187 transform.Translate(dst_rect.x() - src_rect.x(), 188 dst_rect.y() - src_rect.y()); 189 transform.Scale(static_cast<float>(dst_rect.width()) / src_rect.width(), 190 static_cast<float>(dst_rect.height()) / src_rect.height()); 191 return transform; 192 } 193 194 void ScopedTransformOverviewWindow::SetTransform( 195 aura::Window* root_window, 196 const gfx::Transform& transform, 197 bool animate) { 198 DCHECK(overview_started_); 199 200 if (root_window != window_->GetRootWindow()) { 201 if (!window_copies_.empty()) { 202 bool bounds_or_hierarchy_changed = false; 203 aura::Window* window = window_; 204 for (ScopedVector<ScopedWindowCopy>::reverse_iterator iter = 205 window_copies_.rbegin(); 206 !bounds_or_hierarchy_changed && iter != window_copies_.rend(); 207 ++iter, window = GetModalTransientParent(window)) { 208 if (!window) { 209 bounds_or_hierarchy_changed = true; 210 } else if ((*iter)->GetWindow()->GetBoundsInScreen() != 211 window->GetBoundsInScreen()) { 212 bounds_or_hierarchy_changed = true; 213 } 214 } 215 // Clearing the window copies array will force it to be recreated. 216 // TODO(flackr): If only the position changed and not the size, 217 // update the existing window copy's position and continue to use it. 218 if (bounds_or_hierarchy_changed) 219 window_copies_.clear(); 220 } 221 if (window_copies_.empty()) { 222 // TODO(flackr): Create copies of the transient children windows as well. 223 // Currently they will only be visible on the window's initial display. 224 CopyWindowAndTransientParents(root_window, window_); 225 } 226 } 227 SetTransformOnWindowAndTransientChildren(transform, animate); 228 } 229 230 void ScopedTransformOverviewWindow::CopyWindowAndTransientParents( 231 aura::Window* target_root, 232 aura::Window* window) { 233 aura::Window* modal_parent = GetModalTransientParent(window); 234 if (modal_parent) 235 CopyWindowAndTransientParents(target_root, modal_parent); 236 window_copies_.push_back(new ScopedWindowCopy(target_root, window)); 237 } 238 239 void ScopedTransformOverviewWindow::SetTransformOnWindowAndTransientChildren( 240 const gfx::Transform& transform, 241 bool animate) { 242 gfx::Point origin(GetBoundsInScreen().origin()); 243 aura::Window* window = window_; 244 while (::wm::GetTransientParent(window)) 245 window = ::wm::GetTransientParent(window); 246 for (ScopedVector<ScopedWindowCopy>::const_iterator iter = 247 window_copies_.begin(); iter != window_copies_.end(); ++iter) { 248 SetTransformOnWindow( 249 (*iter)->GetWindow(), 250 TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen( 251 (*iter)->GetWindow()->parent(), 252 (*iter)->GetWindow()->GetTargetBounds()).origin() - origin, 253 transform), 254 animate); 255 } 256 SetTransformOnWindowAndAllTransientChildren( 257 window, 258 TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen( 259 window->parent(), window->GetTargetBounds()).origin() - origin, 260 transform), 261 animate); 262 } 263 264 void ScopedTransformOverviewWindow::PrepareForOverview() { 265 DCHECK(!overview_started_); 266 overview_started_ = true; 267 ash::wm::GetWindowState(window_)->set_ignored_by_shelf(true); 268 RestoreWindow(); 269 } 270 271 } // namespace ash 272