1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231

ash / wm / window_cycle / window_cycle_controller.h [blame]

// Copyright 2014 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_WINDOW_CYCLE_WINDOW_CYCLE_CONTROLLER_H_
#define ASH_WM_WINDOW_CYCLE_WINDOW_CYCLE_CONTROLLER_H_

#include <memory>

#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"

namespace aura {
class Window;
}  // namespace aura

namespace ui {
class LocatedEvent;
}  // namespace ui

namespace ash {

class WindowCycleEventFilter;
class WindowCycleList;

// Controls cycling through windows with the keyboard via alt-tab.
// Windows are sorted primarily by most recently used, and then by screen order.
// We activate windows as you cycle through them, so the order on the screen
// may change during the gesture, but the most recently used list isn't updated
// until the cycling ends.  Thus we maintain the state of the windows
// at the beginning of the gesture so you can cycle through in a consistent
// order.
class ASH_EXPORT WindowCycleController : public SessionObserver,
                                         public DesksController::Observer {
 public:
  using WindowList = std::vector<raw_ptr<aura::Window, VectorExperimental>>;

  enum class WindowCyclingDirection { kForward, kBackward };
  enum class KeyboardNavDirection { kUp, kDown, kLeft, kRight, kInvalid };

  // Enumeration of the sources of alt-tab mode switch.
  // Note that these values are persisted to histograms so existing values
  // should remain unchanged and new values should be added to the end.
  enum class ModeSwitchSource { kClick, kKeyboard, kMaxValue = kKeyboard };

  WindowCycleController();

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

  ~WindowCycleController() override;

  // Returns true if cycling through windows is enabled. This is false at
  // certain times, such as when the lock screen is visible.
  static bool CanCycle();

  // Registers alt-tab related profile prefs.
  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  // Returns the WindowCycleList.
  const WindowCycleList* window_cycle_list() const {
    return window_cycle_list_.get();
  }

  // Cycles between windows in the given |direction|. This moves the focus ring
  // to the window in the given |direction| and also scrolls the list. If
  // same_app_only is provided whether or not to cycle exclusively between
  // windows of the same app from now on will be updated.
  void HandleCycleWindow(WindowCyclingDirection direction,
                         bool same_app_only = false);

  // Navigates between cycle windows and tab slider if the move is valid.
  // This moves the focus ring to the active button or the last focused window
  // and announces these changes via ChromeVox.
  void HandleKeyboardNavigation(KeyboardNavDirection direction);

  // Drags the cycle view's mirror container |delta_x|.
  void Drag(float delta_x);

  // Starts a fling for the cycle view's mirror container base on |velocity_x|.
  void StartFling(float velocity_x);

  // Returns true if we are in the middle of a window cycling gesture.
  bool IsCycling() const { return window_cycle_list_.get() != NULL; }

  // Call to start cycling windows. This function adds a pre-target handler to
  // listen to the alt key release.
  void StartCycling(bool same_app_only);

  // Both of these functions stop the current window cycle and removes the event
  // filter. The former indicates success (i.e. the new window should be
  // activated) and the latter indicates that the interaction was cancelled (and
  // the originally active window should remain active).
  void CompleteCycling();
  void CancelCycling();

  // If the window cycle list is open, re-construct it. Do nothing if not
  // cycling.
  void MaybeResetCycleList();

  // Moves the focus ring to |window|. Does not scroll the list. Do nothing if
  // not cycling.
  void SetFocusedWindow(aura::Window* window);

  // Checks whether |event| occurs within the cycle view.
  bool IsEventInCycleView(const ui::LocatedEvent* event) const;

  // Gets the window for the preview item located at |event|. Returns nullptr if
  // |event| is not on the cycle view or a preview item, or |window_cycle_list_|
  // does not exist.
  aura::Window* GetWindowAtPoint(const ui::LocatedEvent* event);

  // Returns whether or not the event is located in tab slider container.
  bool IsEventInTabSliderContainer(const ui::LocatedEvent* event) const;

  // Returns whether or not the window cycle view is visible.
  bool IsWindowListVisible() const;

  // Checks if switching between alt-tab mode via the tab slider is allowed.
  // Returns true if Bento flag is enabled and users have multiple desks.
  bool IsInteractiveAltTabModeAllowed() const;

  // Checks if alt-tab should be per active desk. If
  // `IsInteractiveAltTabModeAllowed()`, alt-tab mode depends on users'
  // |prefs::kAltTabPerDesk| selection. Otherwise, it'll default to all desk
  // unless LimitAltTabToActiveDesk flag is explicitly enabled.
  bool IsAltTabPerActiveDesk() const;

  // Returns true while switching the alt-tab mode and Bento flag is enabled.
  // This helps `Scroll()` and `Step()` distinguish between pressing tabs and
  // switching mode, so they refresh `current_index_` and the focused window
  // correctly.
  bool IsSwitchingMode() const;

  // Returns if the tab slider is currently focused instead of the window cycle
  // during keyboard navigation.
  bool IsTabSliderFocused() const;

  // Saves `per_desk` in the user prefs and announces changes of alt-tab mode
  // and the window selection via ChromeVox. This function is called when the
  // user switches the alt-tab mode via keyboard navigation or button clicking.
  void OnModeChanged(bool per_desk, ModeSwitchSource source);

  // SessionObserver:
  void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;

  // DesksController::Observer:
  void OnDeskAdded(const Desk* desk, bool from_undo) override;
  void OnDeskRemoved(const Desk* desk) override;

 private:
  friend class WindowCycleList;

  // Gets a list of windows from the currently open windows, removing windows
  // with transient roots already in the list. The returned list of windows
  // is used to populate the window cycle list.
  WindowList CreateWindowList();

  // Builds the window list for window cycling, `desks_mru_type` determines
  // whether to include or exclude windows from the inactive desks. The list is
  // built based on `BuildWindowForCycleWithPipList()` and revised so that
  // windows in a snap group are put together with primary window comes before
  // secondary snapped window.
  WindowList BuildWindowListForWindowCycling(DesksMruType desks_mru_type);

  // Populates |active_desk_container_id_before_cycle_| and
  // |active_window_before_window_cycle_| when the window cycle list is
  // initialized.
  void SaveCurrentActiveDeskAndWindow(const WindowList& window_list);

  // Cycles to the next or previous window based on |direction| or to the
  // default position if |starting_alt_tab_or_switching_mode| is true.
  // This updates the focus ring to the window to the right if |direction|
  // is forward or left if backward. If |starting_alt_tab_or_switching_mode| is
  // true and |direction| is forward, the focus ring moves to the first
  // non-active window in MRU list: the second window by default or the first
  // window if it is not active.
  void Step(WindowCyclingDirection direction,
            bool starting_alt_tab_or_switching_mode);

  void StopCycling();

  void InitFromUserPrefs();

  // Triggers alt-tab UI updates when the alt-tab mode is updated in the active
  // user prefs.
  void OnAltTabModePrefChanged();

  // Returns true if the direction is valid regarding the component that the
  // focus is currently on. For example, moving the focus on the top most
  // component, the tab slider button, further up is invalid.
  bool IsValidKeyboardNavigation(KeyboardNavDirection direction) const;

  std::unique_ptr<WindowCycleList> window_cycle_list_;

  // Tracks the ID of the active desk container before window cycling starts. It
  // is used to determine whether a desk switch occurred when cycling ends.
  int active_desk_container_id_before_cycle_ = kShellWindowId_Invalid;

  // Tracks what Window was active when starting to cycle and used to determine
  // if the active Window changed in when ending cycling.
  raw_ptr<aura::Window, DanglingUntriaged> active_window_before_window_cycle_ =
      nullptr;

  // Non-null while actively cycling.
  std::unique_ptr<WindowCycleEventFilter> event_filter_;

  // Tracks whether alt-tab mode is currently switching or not.
  bool is_switching_mode_ = false;

  // The pref service of the currently active user. Can be null in
  // ash_unittests.
  raw_ptr<PrefService> active_user_pref_service_ = nullptr;

  // The pref change registrar to observe changes in prefs value.
  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;

  base::ScopedObservation<DesksController, DesksController::Observer>
      desks_observation_{this};
};

}  // namespace ash

#endif  // ASH_WM_WINDOW_CYCLE_WINDOW_CYCLE_CONTROLLER_H_