| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ |
| #define ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ |
| |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "ui/compositor/layer_animation_observer.h" |
| |
| namespace aura { |
| class Window; |
| } // namespace aura |
| |
| namespace ui { |
| class LayerTreeOwner; |
| } // namespace ui |
| |
| namespace viz { |
| class CopyOutputResult; |
| } // namespace viz |
| |
| namespace ash { |
| |
| class Desk; |
| |
| // Performs the desk switch animation on a root window (i.e. display). Since a |
| // desk spans all displays, one instance of this object will be created for each |
| // display when a new desk is activated. |
| // |
| // Screenshots of the starting and ending desks are taken, and we animate |
| // between them such that the starting desk can appear sliding out of the |
| // screen, while the ending desk is sliding in. We take screenshots to make the |
| // visible state of the desks seem constant to the user (e.g. if the starting |
| // desk is in overview, it appears to remain in overview while sliding out). |
| // This approach makes it possible to show an empty black space separating both |
| // desks while we animate them (See |kDesksSpacing|). |
| // |
| // - `starting` desk: is the currently activated desk which will be deactivated |
| // shortly. |
| // - `ending` desk: is the desk desired to be activated with this animation. |
| // |
| // The animation goes through the following phases: |
| // |
| // - Phase (1) begins by calling TakeStartingDeskScreenshot(), which should be |
| // called before the ending desk is activated. |
| // * Once the screenshot is taken, it is placed in a layer that covers |
| // everything on the screen, so that desk activation can happen without |
| // being seen. |
| // * Delegate::OnStartingDeskScreenshotTaken() is called, and the owner of |
| // this object can check that all animators of all root windows have |
| // finished taking the starting desk screenshots (through checking |
| // starting_desk_screenshot_taken()), upon which the actual ending desk |
| // activation can take place, and phase (2) of the animation can be |
| // triggered. |
| // |
| // - Phase (2) should begin after the ending desk had been activated, |
| // by calling TakeEndingDeskScreenshot(). |
| // * Once the screenshot is taken, it is placed in a sibling layer to the |
| // starting desk screenshot layer, with an offset of |kDesksSpacing| between |
| // the two layers. |
| // * Delegate::OnEndingDeskScreenshotTaken() will be called, upon which the |
| // owner of this object can check if all ending desks screenshots on all |
| // roots are taken by all animators (through checking |
| // ending_desk_screenshot_taken()), so that it can start phase (3) on all of |
| // them at the same time. |
| // |
| // - Phase (3) begins when StartAnimation() is called. |
| // * The parent layer of both screenshot layers is animated, either: |
| // - To the left (move_left_ == true); when the starting desk is on the |
| // left. |
| // |
| // <<<<<-------------------------- move left. |
| // +-----------+ |
| // | Animation | |
| // | layer | |
| // +-----------+ |
| // / \ |
| // +------------+ +------------+ |
| // | start desk | | end desk | |
| // | screenshot | | screenshot | |
| // | layer | | layer | |
| // +------------+ +------------+ |
| // ^ |
| // start here |
| // |
| // Animation layer transforms: |
| // * Begin transform: Identity (since starting desk screeshot is already |
| // visible). |
| // * End transform: Negative translation to the left to slide out starting |
| // desk, and slide in ending desk screenshots into the screen. |
| // |
| // - Or to the right (move_left_ == false), when the starting desk is on the |
| // right. |
| // |
| // move right. -------------------------->>>>> |
| // +-----------+ |
| // | Animation | |
| // | layer | |
| // +-----------+ |
| // / \ |
| // +------------+ +------------+ |
| // | end desk | | start desk | |
| // | screenshot | | screenshot | |
| // | layer | | layer | |
| // +------------+ +------------+ |
| // ^ |
| // start here |
| // |
| // Animation layer transforms: |
| // * Begin transform: Negative translation to the left (to make starting |
| // desk screeshot visible on the screen). |
| // * End transform: Identity to make a translation to the right to slide |
| // out starting desk, and slide in ending desk screenshots into the |
| // screen. |
| // |
| // * The animation always begins such that the starting desk screenshot layer |
| // is the one visible on the screen, and the parent (animation layer) always |
| // moves in the direction such that the ending desk screenshot becomes |
| // visible on the screen. |
| // * Once the animation finishes, Delegate::OnDeskSwitchAnimationFinished() is |
| // triggered. The owner of this object can then check that all animators on |
| // all roots have finished their animations (by checking |
| // animation_finished()), upon which it can delete these animators which |
| // will destroy all the screenshot layers and the real screen contents will |
| // be visible again. |
| // |
| // This cooperative interaction between the animators and their owner |
| // (DesksController) is needed for the following reasons: |
| // 1- The new desk is only activated after all starting desk screenshots on all |
| // roots have been taken and placed on top of everything (between phase (1) |
| // and (2)), so that the effects of desk activation (windows hiding and |
| // showing, overview exiting .. etc.) are not visible to the user. |
| // 2- The animation doesn't start until all ending desk screenshots on all |
| // root windows are ready (between phase (2) and (3)). This is needed to |
| // synchronize the animations on all displays together (otherwise the |
| // animations will lag behind each other). |
| class RootWindowDeskSwitchAnimator : public ui::ImplicitAnimationObserver { |
| public: |
| class Delegate { |
| public: |
| // Called when phase (1) completes. The starting desk screenshot has been |
| // taken and put on the screen. |ending_desk| is the desk that will be |
| // activated after all starting desk screenshots on all roots are taken. |
| virtual void OnStartingDeskScreenshotTaken(const Desk* ending_desk) = 0; |
| |
| // Called when phase (2) completes. The ending desk screenshot has been |
| // taken and put on the screen. |
| virtual void OnEndingDeskScreenshotTaken() = 0; |
| |
| // Called when phase (3) completes. The animation completes and the ending |
| // desk screenshot is now showing on the screen. |
| virtual void OnDeskSwitchAnimationFinished() = 0; |
| |
| protected: |
| virtual ~Delegate() = default; |
| }; |
| |
| RootWindowDeskSwitchAnimator(aura::Window* root, |
| const Desk* ending_desk, |
| Delegate* delegate, |
| bool move_left); |
| |
| ~RootWindowDeskSwitchAnimator() override; |
| |
| bool starting_desk_screenshot_taken() const { |
| return starting_desk_screenshot_taken_; |
| } |
| bool ending_desk_screenshot_taken() const { |
| return ending_desk_screenshot_taken_; |
| } |
| bool animation_finished() const { return animation_finished_; } |
| |
| // Begins pahse (1) of the animation by taking a screenshot of the starting |
| // desk content. Delegate::OnStartingDeskScreenshotTaken() will be called once |
| // the screenshot is taken and placed on top of everything on the screen. |
| void TakeStartingDeskScreenshot(); |
| |
| // Begins phase (2) of the animation, after the ending desk has already |
| // been activated. Delegate::OnEndingDeskScreenshotTaken() will be called once |
| // the screenshot is taken. |
| void TakeEndingDeskScreenshot(); |
| |
| // Begins phase (3) of the animation by actually animating the screenshot |
| // layers such that we have a movement from the starting desk screenshot |
| // towards the ending desk screenshot. |
| // Delegate::OnDeskSwitchAnimationFinished() will be called once the animation |
| // finishes. |
| void StartAnimation(); |
| |
| // ui::ImplicitAnimationObserver: |
| void OnImplicitAnimationsCompleted() override; |
| |
| private: |
| void OnStartingDeskScreenshotTaken( |
| std::unique_ptr<viz::CopyOutputResult> copy_result); |
| void OnEndingDeskScreenshotTaken( |
| std::unique_ptr<viz::CopyOutputResult> copy_result); |
| |
| // The root window that this animator is associated with. |
| aura::Window* const root_window_; |
| |
| // The desk to activate and animate to with this animator. |
| const Desk* const ending_desk_; |
| |
| Delegate* const delegate_; |
| |
| // The owner of the layer tree that contains the parent "animation layer" and |
| // both its child starting and ending desks "screenshot layers". |
| std::unique_ptr<ui::LayerTreeOwner> animation_layer_owner_; |
| |
| // The amount by which the animation layer will be translated horizontally |
| // either startingly or at the end of the animation, depending on the value of |
| // |move_left_|. |
| const int x_translation_offset_; |
| |
| // Number of retires for taking the starting and ending screenshots, if we |
| // get an empty result. |
| int starting_desk_screenshot_retries_ = 0; |
| int ending_desk_screenshot_retries_ = 0; |
| |
| // True when the animation layer should be translated towards the left, which |
| // means the starting desk is on the left of the ending desk. |
| const bool move_left_; |
| |
| // True when phase (1) finishes. |
| bool starting_desk_screenshot_taken_ = false; |
| |
| // True when phase (2) finishes. |
| bool ending_desk_screenshot_taken_ = false; |
| |
| // True when phase (3) finishes. |
| bool animation_finished_ = false; |
| |
| base::WeakPtrFactory<RootWindowDeskSwitchAnimator> weak_ptr_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(RootWindowDeskSwitchAnimator); |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_WM_DESKS_ROOT_WINDOW_DESK_SWITCH_ANIMATOR_H_ |