1 // Copyright 2019 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 #ifndef ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ 6 #define ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ 7 8 #include <memory> 9 10 #include "ash/ash_export.h" 11 #include "base/callback.h" 12 #include "base/memory/weak_ptr.h" 13 #include "ui/compositor/layer_animation_observer.h" 14 15 namespace aura { 16 class Window; 17 } // namespace aura 18 19 namespace ui { 20 class LayerTreeOwner; 21 class Layer; 22 } // namespace ui 23 24 namespace viz { 25 class CopyOutputResult; 26 } // namespace viz 27 28 namespace ash { 29 30 // Performs the desk switch animation on a root window (i.e. display). Since a 31 // desk spans all displays, one instance of this object will be created for each 32 // display when a new desk is activated. 33 // 34 // Screenshots of the starting and ending desks are taken, and we animate 35 // between them such that the starting desk can appear sliding out of the 36 // screen, while the ending desk is sliding in. We take screenshots to make the 37 // visible state of the desks seem constant to the user (e.g. if the starting 38 // desk is in overview, it appears to remain in overview while sliding out). 39 // This approach makes it possible to show an empty black space separating both 40 // desks while we animate them (See |kDesksSpacing|). The ending desk may change 41 // after the animation has started. In this case, a new animation will replace 42 // the current one and animate to the new ending desk, requesting a new 43 // screenshot if necessary. 44 // - `starting` desk: is the currently activated desk which will be deactivated 45 // shortly. 46 // - `ending` desk: is the desk desired to be activated with this animation. 47 // These can be changed if the enhanced desk animations feature is enabled using 48 // ReplaceAnimation() or UpdateSwipeAnimation(). 49 // 50 // The animation goes through the following phases: 51 // 52 // - Phase (1) begins by calling TakeStartingDeskScreenshot(), which should be 53 // called before the ending desk is activated. 54 // * Once the screenshot is taken, it is placed in a layer that covers 55 // everything on the screen, so that desk activation can happen without 56 // being seen. 57 // * Delegate::OnStartingDeskScreenshotTaken() is called, and the owner of 58 // this object can check that all animators of all root windows have 59 // finished taking the starting desk screenshots (through checking 60 // starting_desk_screenshot_taken()), upon which the actual ending desk 61 // activation can take place, and phase (2) of the animation can be 62 // triggered. 63 // 64 // - Phase (2) should begin after the ending desk had been activated, 65 // by calling TakeEndingDeskScreenshot(). 66 // * Once the screenshot is taken, it is placed in a sibling layer to the 67 // starting desk screenshot layer, with an offset of |kDesksSpacing| between 68 // the two layers. 69 // * Delegate::OnEndingDeskScreenshotTaken() will be called, upon which the 70 // owner of this object can check if all ending desks screenshots on all 71 // roots are taken by all animators (through checking 72 // ending_desk_screenshot_taken()), so that it can start phase (3) on all of 73 // them at the same time. 74 // * Phase (2) can be rentered after starting phase (3) by calling 75 // ReplaceAnimation() or UpdateSwipeAnimation(). The new ending desk will 76 // change, and if it does not have an associated screenshot layer, the 77 // caller will be responsible for requesting one using 78 // TakeEndingDeskScreenshot(). The screenshots are taken as needed since 79 // their layers are fullscreen and require activating a desk which may be a 80 // large operation for something that the user may not see. Once the 81 // screenshot is taken, it is kept until |this| is destroyed. If an 82 // associated screenshot layer exists already, ReplaceAnimation() and 83 // UpdateSwipeAnimation() can proceed without returning to phase (2). 84 // 85 // - Phase (3) begins when StartAnimation() is called. 86 // * The parent layer of both screenshot layers is animated, either: 87 // - To the left (starting_desk_index_ < ending_desk_index_); when the 88 // starting desk is on the left. 89 // 90 // <<<<<-------------------------- move left. 91 // +-----------+ 92 // | Animation | 93 // | layer | 94 // +-----------+ 95 // / \ 96 // +------------+ +------------+ 97 // | start desk | | end desk | 98 // | screenshot | | screenshot | 99 // | layer | | layer | 100 // +------------+ +------------+ 101 // ^ 102 // start here 103 // 104 // Animation layer transforms: 105 // * Begin transform: The transform that will make the starting desk 106 // screenshot visible. In this case it is a transform with translation 107 // (edge_padding_width_dp_, 0). 108 // * End transform: The transform that will make the ending desk 109 // screenshot visible. In this case it is a transform with translation 110 // (-|edge_padding_width_dp_| - |x_translation_offset_| - 111 // |kDesksSpacing|, 0). 112 // 113 // - Or to the right (starting_desk_index_ > ending_desk_index_), when the 114 // starting desk is on the right. 115 // 116 // move right. -------------------------->>>>> 117 // +-----------+ 118 // | Animation | 119 // | layer | 120 // +-----------+ 121 // / \ 122 // +------------+ +------------+ 123 // | end desk | | start desk | 124 // | screenshot | | screenshot | 125 // | layer | | layer | 126 // +------------+ +------------+ 127 // ^ 128 // start here 129 // 130 // Animation layer transforms: 131 // * Begin transform: The transform that will make the starting desk 132 // screenshot visible. In this case it is a transform with translation 133 // (-|edge_padding_width_dp_| - |x_translation_offset_| - 134 // |kDesksSpacing|, 0). 135 // * End transform: The transform that will make the ending desk 136 // screenshot visible. In this case it is a transform with translation 137 // (edge_padding_width_dp_, 0). 138 // 139 // * The animation always begins such that the starting desk screenshot layer 140 // is the one visible on the screen, and the parent (animation layer) always 141 // moves in the direction such that the ending desk screenshot becomes 142 // visible on the screen. 143 // * The children (screenshot layers) are always placed left to right to match 144 // desk order. For example, if there are three desks and this class has been 145 // instructed to create a screenshot for all three desks, desk 1's 146 // screenshot will be on the left, desk 2's screenshot will be in the middle 147 // and desk 3's screenshot will be on the right. 148 // * Once the animation finishes, Delegate::OnDeskSwitchAnimationFinished() is 149 // triggered. The owner of this object can then check that all animators on 150 // all roots have finished their animations (by checking 151 // animation_finished()), upon which it can delete these animators which 152 // will destroy all the screenshot layers and the real screen contents will 153 // be visible again. 154 // 155 // This cooperative interaction between the animators and their owner 156 // (DesksController::AbstractDeskSwitchAnimation) is needed for the following 157 // reasons: 158 // 1- The new desk is only activated after all starting desk screenshots on all 159 // roots have been taken and placed on top of everything (between phase (1) 160 // and (2)), so that the effects of desk activation (windows hiding and 161 // showing, overview exiting .. etc.) are not visible to the user. 162 // 2- The animation doesn't start until all ending desk screenshots on all 163 // root windows are ready (between phase (2) and (3)). This is needed to 164 // synchronize the animations on all displays together (otherwise the 165 // animations will lag behind each other). 166 // 167 // When this animator is used to implement the remove-active-desk animation 168 // (which also involves switching desks; from the to-be-removed desk to another 169 // desk), `for_remove` is set to true in the constructor. The animation is 170 // slightly tweaked to do the following: 171 // - Instead of taking a screenshot of the starting desk, we replace it by a 172 // black solid color layer, to indicate the desk is being removed. 173 // - The layer tree of the active-desk container is recreated, and the old 174 // layers are detached and animated vertically by 175 // `kRemovedDeskWindowYTranslation`. 176 // - That old layer tree is then translated back down by the same amount while 177 // the desks screenshots are animating horizontally. 178 // This gives the effect that the removed desk windows are jumping from their 179 // desk to the target desk. 180 class ASH_EXPORT RootWindowDeskSwitchAnimator 181 : public ui::ImplicitAnimationObserver { 182 public: 183 class Delegate { 184 public: 185 // Called when phase (1) completes. The starting desk screenshot has been 186 // taken and put on the screen. |ending_desk_index| is the index of the desk 187 // that will be activated after all starting desk screenshots on all roots 188 // are taken. 189 virtual void OnStartingDeskScreenshotTaken(int ending_desk_index) = 0; 190 191 // Called when phase (2) completes. The ending desk screenshot has been 192 // taken and put on the screen. This can be called multiple times during the 193 // lifetime of |this|. 194 virtual void OnEndingDeskScreenshotTaken() = 0; 195 196 // Called when phase (3) completes. The animation completes and the ending 197 // desk screenshot is now showing on the screen. 198 virtual void OnDeskSwitchAnimationFinished() = 0; 199 200 // Called while doing a continuous gesture to notify when the desk that is 201 // visible to the user has changed. Used for metrics collection. 202 virtual void OnVisibleDeskChanged() = 0; 203 204 protected: 205 virtual ~Delegate() = default; 206 }; 207 208 // The space between the starting and ending desks screenshots in dips. 209 static constexpr int kDesksSpacing = 50; 210 211 // The animation layer has extra padding at its two edges. The width in dips 212 // is a ratio of the root window width. This padding is to notify users there 213 // are no more desks on that side by showing a black region as we swipe 214 // continuously. 215 static constexpr float kEdgePaddingRatio = 0.15f; 216 217 // In touchpad units, a touchpad swipe of this length will correspond to a 218 // full desk change. 219 static constexpr int kTouchpadSwipeLengthForDeskChange = 420; 220 221 RootWindowDeskSwitchAnimator(aura::Window* root, 222 int starting_desk_index, 223 int ending_desk_index, 224 Delegate* delegate, 225 bool for_remove); 226 RootWindowDeskSwitchAnimator(const RootWindowDeskSwitchAnimator&) = delete; 227 RootWindowDeskSwitchAnimator& operator=(const RootWindowDeskSwitchAnimator&) = 228 delete; 229 ~RootWindowDeskSwitchAnimator() override; 230 ending_desk_index()231 int ending_desk_index() const { return ending_desk_index_; } 232 starting_desk_screenshot_taken()233 bool starting_desk_screenshot_taken() const { 234 return starting_desk_screenshot_taken_; 235 } ending_desk_screenshot_taken()236 bool ending_desk_screenshot_taken() const { 237 return ending_desk_screenshot_taken_; 238 } animation_finished()239 bool animation_finished() const { return animation_finished_; } 240 241 // Begins phase (1) of the animation by taking a screenshot of the starting 242 // desk content. Delegate::OnStartingDeskScreenshotTaken() will be called once 243 // the screenshot is taken and placed on top of everything on the screen. 244 void TakeStartingDeskScreenshot(); 245 246 // Begins phase (2) of the animation, after the ending desk has already 247 // been activated. Delegate::OnEndingDeskScreenshotTaken() will be called once 248 // the screenshot is taken. 249 void TakeEndingDeskScreenshot(); 250 251 // Begins phase (3) of the animation by actually animating the screenshot 252 // layers such that we have a movement from the starting desk screenshot 253 // towards the ending desk screenshot. 254 // Delegate::OnDeskSwitchAnimationFinished() will be called once the animation 255 // finishes. 256 void StartAnimation(); 257 258 // Replace the current animation with one that goes to 259 // |new_ending_desk_index|. Returns true if a screenshot of the new desk needs 260 // to be taken. 261 bool ReplaceAnimation(int new_ending_desk_index); 262 263 // Called as a user is performing a touchpad swipe. Requests a new screenshot 264 // if necessary based on the last direction as specified in |scroll_delta_x|. 265 // |scroll_delta_x| is in touchpad units, it will be converted to display 266 // units and then used to shift the animation layer. If the animation layer is 267 // near its boundaries, this will return an index for the desk we should take 268 // a screenshot for. If we are not near the boundaries, or if there is no next 269 // adjacent desk in the direction we are heading, return base::nullopt. The 270 // delegate is responsible for requesting the screenshot. 271 base::Optional<int> UpdateSwipeAnimation(float scroll_delta_x); 272 273 // Maybe called after UpdateSwipeAnimation() if we need a new screenshot. 274 // Updates |ending_desk_index_| and resets some other internal state related 275 // to the ending desk screenshot. 276 void PrepareForEndingDeskScreenshot(int new_ending_desk_index); 277 278 // Called when a user ends a touchpad swipe. This will animate to the most 279 // visible desk, whose index is also returned. 280 int EndSwipeAnimation(); 281 282 // ui::ImplicitAnimationObserver: 283 void OnImplicitAnimationsCompleted() override; 284 285 ui::Layer* GetAnimationLayerForTesting() const; 286 287 private: 288 friend class RootWindowDeskSwitchAnimatorTestApi; 289 290 // Completes the first phase of the animation using the given |layer| as the 291 // screenshot layer of the starting desk. This layer will be parented to the 292 // animation layer, which will be setup with its initial transform according 293 // to |starting_desk_index_| and |ending_desk_index_|. If |for_remove_| is 294 // true, the detached old layer tree of the soon-to-be-removed-desk's windows 295 // will be translated up vertically to simulate a jump from the removed desk 296 // to the target desk. |Delegate::OnStartingDeskScreenshotTaken()| will be 297 // called at the end. 298 void CompleteAnimationPhase1WithLayer(std::unique_ptr<ui::Layer> layer); 299 300 void OnStartingDeskScreenshotTaken( 301 std::unique_ptr<viz::CopyOutputResult> copy_result); 302 void OnEndingDeskScreenshotTaken( 303 std::unique_ptr<viz::CopyOutputResult> copy_result); 304 305 // Called when a screenshot layer is created and added to the animation layer. 306 // Sets its bounds and transforms the animation layer to the correct starting 307 // position. 308 void OnScreenshotLayerCreated(); 309 310 // Gets the x position of the |screenshot_layer_| associated with |index| in 311 // its parent layer's coordinates (|animation_layer_owner_->root()|). 312 int GetXPositionOfScreenshot(int index); 313 314 // Gets the index of the desk whose screenshot of the animation layer is most 315 // visible to the user. That desk screenshot is the one which aligns the most 316 // with the root window bounds. 317 int GetIndexOfMostVisibleDeskScreenshot() const; 318 319 // The root window that this animator is associated with. 320 aura::Window* const root_window_; 321 322 // The index of the active desk at the start of the animation. 323 int starting_desk_index_; 324 325 // The index of the desk to activate and animate to with this animator. 326 int ending_desk_index_; 327 328 // The index of the desk that is most visible to the user based on the 329 // transform of the animation layer. 330 int visible_desk_index_; 331 332 Delegate* const delegate_; 333 334 // The owner of the layer tree of the old detached layers of the removed 335 // desk's windows. This is only valid if |for_remove_| is true. This layer 336 // tree is animated to simulate that the windows are jumping from the removed 337 // desk to the target desk. 338 std::unique_ptr<ui::LayerTreeOwner> old_windows_layer_tree_owner_; 339 340 // The owner of the layer tree that contains the parent "animation layer" and 341 // both its child starting and ending desks "screenshot layers". 342 std::unique_ptr<ui::LayerTreeOwner> animation_layer_owner_; 343 344 // Stores the layers of taken screenshots. This vector is the same size as 345 // desks_util::kMaxNumberOfDesks and the screenshot at index i will correspond 346 // to desk i but the layers will be nullptr until they are needed. For 347 // example, for a desk activation animation from desk index 0 -> 1 will have 348 // screenshots of desk 0 and desk 1 stored at indices 0 and 1, but the 349 // remaining indices will have nullptr. The layers, if not null are owned by 350 // |animation_layer_owner_|. 351 std::vector<ui::Layer*> screenshot_layers_; 352 353 // Stores the size of |root_window_| that takes into account all scale factors 354 // by snapping to the edge of the display. This will prevent any 1px gaps we 355 // may see while switching desks. Prefer to use this in all calculations over 356 // |root_window_| get bounds functions. 357 const gfx::Size root_window_size_; 358 359 // The amount by which the animation layer will be translated horizontally 360 // either startingly or at the end of the animation, depending on the value of 361 // of the desk indices. 362 const int x_translation_offset_; 363 364 // The amount of padding in dips on the edges of the animation layer. 365 const int edge_padding_width_dp_; 366 367 // Number of retires for taking the starting and ending screenshots, if we 368 // get an empty result. 369 int starting_desk_screenshot_retries_ = 0; 370 int ending_desk_screenshot_retries_ = 0; 371 372 // True if this animator is handling the remove-active-desk animation. 373 const bool for_remove_; 374 375 // True when phase (1) finishes. 376 bool starting_desk_screenshot_taken_ = false; 377 378 // True when phase (2) finishes. 379 bool ending_desk_screenshot_taken_ = false; 380 381 // True when phase (3) finishes. 382 bool animation_finished_ = false; 383 384 // True while setting a new transform for chaining. If a animation is active, 385 // calling SetTransform will trigger OnImplicitAnimationsCompleted. In these 386 // cases we do not want to notify our delegate that the animation is finished. 387 bool setting_new_transform_ = false; 388 389 // Callback that is run after the ending screenshot is taken for testing 390 // purposes. 391 base::OnceClosure on_ending_screenshot_taken_callback_for_testing_; 392 393 base::WeakPtrFactory<RootWindowDeskSwitchAnimator> weak_ptr_factory_{this}; 394 }; 395 396 } // namespace ash 397 398 #endif // ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ 399