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