chromium/ash/wm/overview/overview_controller.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_OVERVIEW_OVERVIEW_CONTROLLER_H_
#define ASH_WM_OVERVIEW_OVERVIEW_CONTROLLER_H_

#include <memory>
#include <vector>

#include "ash/ash_export.h"
#include "ash/wm/overview/delayed_animation_observer.h"
#include "ash/wm/overview/overview_delegate.h"
#include "ash/wm/overview/overview_metrics.h"
#include "ash/wm/overview/overview_observer.h"
#include "ash/wm/overview/overview_session_metrics_recorder.h"
#include "ash/wm/overview/overview_types.h"
#include "ash/wm/overview/overview_window_occlusion_calculator.h"
#include "ash/wm/raster_scale/raster_scale_controller.h"
#include "base/cancelable_callback.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/activation_change_observer.h"

namespace ash {

class OverviewSession;

// Manages a overview session which displays an overview of all windows and
// allows selecting a window to activate it.
class ASH_EXPORT OverviewController : public OverviewDelegate,
                                      public wm::ActivationChangeObserver {
 public:
  // `ScopedOcclusionPauser` pauses occlusion tracking for overview mode
  // purposes until it is destroyed. When it is destroyed, occlusion tracking
  // will be unpaused after the given unpause delay. If a
  // `ScopedOcclusionPauser`s is destroyed while an unpause delay is in
  // progress, that delay will be cancelled and the new delay will be used. This
  // means that if two `ScopedOcclusionPauser`s are destroyed, the delay for the
  // second `ScopedOcclusionPauser` to be destroyed will be used, even if the
  // first delay is much longer.
  class ScopedOcclusionPauser {
   public:
    ScopedOcclusionPauser(ScopedOcclusionPauser&&);
    ScopedOcclusionPauser& operator=(ScopedOcclusionPauser&&);

    ScopedOcclusionPauser(const ScopedOcclusionPauser&) = delete;
    ScopedOcclusionPauser& operator=(const ScopedOcclusionPauser&) = delete;

    ~ScopedOcclusionPauser();

   private:
    friend class OverviewController;

    ScopedOcclusionPauser(base::WeakPtr<OverviewController> controller,
                          base::TimeDelta unpause_delay);

    base::WeakPtr<OverviewController> controller_;
    base::TimeDelta unpause_delay_;
  };

  OverviewController();

  OverviewController(const OverviewController&) = delete;
  OverviewController& operator=(const OverviewController&) = delete;

  ~OverviewController() override;

  [[nodiscard]] ScopedOcclusionPauser PauseOcclusionTracker(
      base::TimeDelta unpause_delay);

  // Convenience function to get the overview controller instance, which is
  // created and owned by Shell.
  static OverviewController* Get();

  OverviewSession* overview_session() { return overview_session_.get(); }

  bool disable_app_id_check_for_saved_desks() const {
    return disable_app_id_check_for_saved_desks_;
  }

  bool is_continuous_scroll_in_progress() const {
    return is_continuous_scroll_in_progress_;
  }

  bool windows_have_snapshot() const { return windows_have_snapshot_; }

  // Starts/Ends overview with `type`. Returns true if enter or exit overview
  // successful. Depending on `type` the enter/exit animation will look
  // different. `start_action`/`end_action` is used by UMA to record the reasons
  // that trigger overview starts or ends. E.g, pressing the overview button.
  bool StartOverview(
      OverviewStartAction start_action,
      OverviewEnterExitType type = OverviewEnterExitType::kNormal);
  bool EndOverview(OverviewEndAction end_action,
                   OverviewEnterExitType type = OverviewEnterExitType::kNormal);

  // Returns true if it's possible to enter overview mode in the current
  // configuration. This can be false at certain times, such as when the lock
  // screen is visible we can't enter overview mode.
  bool CanEnterOverview() const;

  // Returns true if overview mode is active.
  bool InOverviewSession() const;

  // Receives a continuous scroll event from the gesture handler and either
  // initializes overview mode in preparation for future continuous scrolls, or
  // immediately calls `OverviewGrid::PositionWindowsForContinuousScrolls()` if
  // overview mode has already been initialized.
  bool HandleContinuousScroll(float y_offset, OverviewEnterExitType type);

  // Moves the current selection forward or backward.
  void IncrementSelection(bool forward);

  // Accepts current selection if any. Returns true if a selection was made,
  // false otherwise.
  bool AcceptSelection();

  // Returns true if we're in start-overview animation.
  bool IsInStartAnimation();

  // Returns true if overview has been shutdown, but is still animating to the
  // end state ui.
  bool IsCompletingShutdownAnimations() const;

  void AddObserver(OverviewObserver* observer);
  void RemoveObserver(OverviewObserver* observer);

  // Post a task to update the shadow and rounded corners of overview windows.
  void DelayedUpdateRoundedCornersAndShadow();

  // OverviewDelegate:
  void AddExitAnimationObserver(
      std::unique_ptr<DelayedAnimationObserver> animation) override;
  void RemoveAndDestroyExitAnimationObserver(
      DelayedAnimationObserver* animation) override;
  void AddEnterAnimationObserver(
      std::unique_ptr<DelayedAnimationObserver> animation_observer) override;
  void RemoveAndDestroyEnterAnimationObserver(
      DelayedAnimationObserver* animation_observer) override;

  // ::wm::ActivationChangeObserver:
  void OnWindowActivating(ActivationReason reason,
                          aura::Window* gained_active,
                          aura::Window* lost_active) override;
  void OnWindowActivated(ActivationReason reason,
                         aura::Window* gained_active,
                         aura::Window* lost_active) override {}

  base::AutoReset<bool> SetDisableAppIdCheckForTests();

  void set_occlusion_pause_duration_for_start_for_test(
      base::TimeDelta duration) {
    occlusion_pause_duration_for_start_ = duration;
  }
  void set_occlusion_pause_duration_for_end_for_test(base::TimeDelta duration) {
    occlusion_pause_duration_for_end_ = duration;
  }
  void set_delayed_animation_task_delay_for_test(base::TimeDelta delta) {
    delayed_animation_task_delay_ = delta;
  }

  void set_windows_have_snapshot_for_test(bool windows_have_snapshot) {
    windows_have_snapshot_ = windows_have_snapshot;
  }

 private:
  // Toggle overview mode. Depending on |type| the enter/exit animation will
  // look different.
  void ToggleOverview(
      OverviewEnterExitType type = OverviewEnterExitType::kNormal);

  // Returns true if it's possible to exit overview mode in the current
  // configuration. This can be false at certain times, such as when the divider
  // or desks are animating.
  bool CanEndOverview(OverviewEnterExitType type) const;

  void OnStartingAnimationComplete(bool canceled);
  void OnEndingAnimationComplete(bool canceled);

  void UpdateRoundedCornersAndShadow();

  // Pause or unpause the occlusion tracker. Resets the unpause delay if we were
  // already in the process of unpausing.
  void MaybePauseOcclusionTracker();
  void MaybeUnpauseOcclusionTracker(base::TimeDelta delay);
  void ResetPauser();

  // Collection of DelayedAnimationObserver objects that own widgets that may be
  // still animating after overview mode ends. If shell needs to shut down while
  // those animations are in progress, the animations are shut down and the
  // widgets destroyed.
  std::vector<std::unique_ptr<DelayedAnimationObserver>> delayed_animations_;
  // Collection of DelayedAnimationObserver objects. When this becomes empty,
  // notify shell that the starting animations have been completed.
  std::vector<std::unique_ptr<DelayedAnimationObserver>> start_animations_;

  // Indicates that overview shall gain focus when the starting animations have
  // completed.
  bool should_focus_overview_ = false;

  // Used when feature ContinuousOverviewScrollAnimation is enabled to
  // determine the start/end positions of overview items as well as their shadow
  // bounds and corner radii during a continuous scroll. It's true only if the
  // last scroll event was the start of a continuous scroll or a continuous
  // scroll update that is within the threshold.
  bool is_continuous_scroll_in_progress_ = false;

  // We may pause occlusion tracking on enter and exit overview mode.
  std::optional<ScopedOcclusionPauser> enter_pauser_;
  std::optional<ScopedOcclusionPauser> exit_pauser_;

  // The following state tracks occlusion pausing and its delayed unpausing for
  // overview mode.
  int pause_count_ = 0;
  std::unique_ptr<aura::WindowOcclusionTracker::ScopedPause>
      occlusion_tracker_pauser_;
  base::CancelableOnceClosure reset_pauser_task_;

  // In order to guarantee relative ordering between occlusion updates and
  // raster scale updates, we need to pause raster scale updates sometimes.
  std::optional<ScopedPauseRasterScaleUpdates> raster_scale_pauser_;

  std::unique_ptr<OverviewSession> overview_session_;

  base::Time last_overview_session_time_;

  base::TimeDelta occlusion_pause_duration_for_start_;
  base::TimeDelta occlusion_pause_duration_for_end_;

  // App dragging enters overview right away. This task is used to delay the
  // |OnStartingAnimationComplete| call so that some animations do not make the
  // initial setup less performant.
  base::TimeDelta delayed_animation_task_delay_;

  base::ObserverList<OverviewObserver> observers_;

  std::unique_ptr<views::Widget::PaintAsActiveLock> paint_as_active_lock_;

  // In ash unittests, the `FullRestoreSaveHandler` isn't hooked up so
  // initialized windows lack an app id. If a window doesn't have a valid app
  // id, then it won't be tracked by `OverviewGrid` as a supported window and
  // those windows will be deemed unsupported for Saved Desks. If
  // `disable_app_id_check_for_saved_desks_` is true, then this check is
  // omitted so we can test Saved Desks.
  bool disable_app_id_check_for_saved_desks_ = false;

  // True if windows shown in overview mode will have a snapshot available.
  // If a snapshot is available then we can pause occlusion tracking until
  // overview mode as finished its enter animation. Otherwise, we must mark
  // all windows as visible immediately.
  bool windows_have_snapshot_ = false;

  std::optional<OverviewSessionMetricsRecorder> session_metrics_recorder_;

  OverviewWindowOcclusionCalculator overview_window_occlusion_calculator_;

  base::WeakPtrFactory<OverviewController> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_WM_OVERVIEW_OVERVIEW_CONTROLLER_H_