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

ash / wm / snap_group / snap_group_controller.h [blame]

// Copyright 2023 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_SNAP_GROUP_SNAP_GROUP_CONTROLLER_H_
#define ASH_WM_SNAP_GROUP_SNAP_GROUP_CONTROLLER_H_

#include <memory>
#include <vector>

#include "ash/ash_export.h"
#include "ash/wm/overview/overview_observer.h"
#include "ash/wm/snap_group/snap_group_metrics.h"
#include "ash/wm/wm_metrics.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "ui/aura/window.h"
#include "ui/display/display_observer.h"

namespace display {
enum class TabletState;
}  // namespace display

namespace ash {

class SnapGroup;
class SnapGroupObserver;

// Works as the centralized place to manage the `SnapGroup`. A single instance
// of this class will be created and owned by `Shell`. It controls the creation
// and destruction of the `SnapGroup`.
class ASH_EXPORT SnapGroupController : public OverviewObserver,
                                       public display::DisplayObserver {
 public:
  using SnapGroups = std::vector<std::unique_ptr<SnapGroup>>;
  using WindowToSnapGroupMap =
      base::flat_map<aura::Window*, raw_ptr<SnapGroup, CtnExperimental>>;

  SnapGroupController();
  SnapGroupController(const SnapGroupController&) = delete;
  SnapGroupController& operator=(const SnapGroupController&) = delete;
  ~SnapGroupController() override;

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

  // Returns true if `window1` and `window2` are in the same snap group.
  bool AreWindowsInSnapGroup(aura::Window* window1,
                             aura::Window* window2) const;

  // Called by `SplitViewController` when `window` is snapped. Returns true if
  // `window` was added to a group, either by normal group creation or snap
  // to replace.
  bool OnWindowSnapped(aura::Window* window,
                       WindowSnapActionSource snap_action_source);

  // Attempts to add `window1` and `window2` as a `SnapGroup`. Returns the
  // `SnapGroup`, if the creation is successful. Returns nullptr, otherwise.
  // Currently, both windows must reside within the same parent container for
  // successful creation. If `replace` is true, the group was snapped to replace
  // and we shouldn't record the count change. `carry_over_creation_time`
  // indicates the creation time of a prior Snap Group from which the current
  // one was derived using the Snap to Replace feature.
  // TODO(b/333772909): Remove `replace` param when snap to replace updates
  // window in SnapGroup instead of removing and re-adding a SnapGroup.
  SnapGroup* AddSnapGroup(
      aura::Window* window1,
      aura::Window* window2,
      bool replace,
      std::optional<base::TimeTicks> carry_over_creation_time);

  // Removes the specified `snap_group`, recording the `exit_point` metric.
  // Returns true if the corresponding `snap_group` has been successfully
  // removed from the `snap_groups_` and `window_to_snap_group_map_`. False
  // otherwise.
  bool RemoveSnapGroup(SnapGroup* snap_group, SnapGroupExitPoint exit_point);

  // Returns true if the corresponding snap group that contains the
  // given `window` has been removed successfully. Returns false otherwise.
  bool RemoveSnapGroupContainingWindow(aura::Window* window,
                                       SnapGroupExitPoint exit_point);

  // Returns the corresponding `SnapGroup` if the given `window` belongs to a
  // snap group or nullptr otherwise.
  SnapGroup* GetSnapGroupForGivenWindow(const aura::Window* window) const;

  // Returns the topmost fully visible non-occluded snap group on `target_root`.
  SnapGroup* GetTopmostVisibleSnapGroup(const aura::Window* target_root) const;

  // Returns the topmost snap group in unminimized state.
  SnapGroup* GetTopmostSnapGroup() const;

  // Determines which windows can be used for snap-to-replace with keyboard
  // shortcut:
  // 1. Finds the topmost snapped window.
  // 2. Identifies the window within a partially obscured Snap Group that isn't
  // hidden by the topmost snapped window.
  //  Returns the window pair for snap-to-replace: [primary snapped window,
  //  secondary snapped window].
  std::optional<std::pair<aura::Window*, aura::Window*>>
  GetWindowPairForSnapToReplaceWithKeyboardShortcut();

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

  // Called by `WindowState` when a float or unfloat event for `window` has
  // completed.
  void OnFloatUnfloatCompleted(aura::Window* window);

  // OverviewObserver:
  void OnOverviewModeStarting() override;
  void OnOverviewModeEnding(OverviewSession* overview_session) override;
  void OnOverviewModeEndingAnimationComplete(bool canceled) override;

  // display::DisplayObserver:
  void OnDisplayTabletStateChanged(display::TabletState state) override;

  const SnapGroups& snap_groups_for_testing() const { return snap_groups_; }
  const WindowToSnapGroupMap& window_to_snap_group_map_for_testing() const {
    return window_to_snap_group_map_;
  }

 private:
  // Returns true if the attempt to replace the window within the snap group of
  // `opposite_snapped_window` positioned directly below with the given
  // `to_be_snapped_window` is successful, returns false otherwise. The
  // `snap_action_source` determines the need for snap ratio difference
  // calculations during 'snap to replace'.
  bool MaybeSnapToReplace(aura::Window* to_be_snapped_window,
                          aura::Window* opposite_snapped_window,
                          WindowSnapActionSource snap_action_source);

  // Retrieves the other window that is in the same snap group if any. Returns
  // nullptr if such window can't be found i.e. the window is not in a snap
  // group.
  aura::Window* RetrieveTheOtherWindowInSnapGroup(aura::Window* window) const;

  // Restores the snapped state of the `snap_groups_` upon completion of certain
  // transitions such as overview mode or tablet mode. Disallow overview to be
  // shown on the other side of the screen when restoring snap groups so that
  // the restore will be instant and the recursive snapping behavior will be
  // avoided.
  void RestoreSnapGroups();

  // Restore the snap state of the windows in the given `snap_group`.
  void RestoreSnapState(SnapGroup* snap_group);

  // Called when the display tablet state is changed.
  void OnTabletModeStarted();

  // Contains all the `SnapGroup`(s), we will have one `SnapGroup` globally for
  // the first iteration but will have multiple in the future iteration.
  SnapGroups snap_groups_;

  // Maps the `SnapGroup` by the `aura::Window*`. It will be used to get the
  // `SnapGroup` with the `aura::Window*` and can also be used to decide if a
  // window is in a `SnapGroup` or not.
  WindowToSnapGroupMap window_to_snap_group_map_;

  base::ObserverList<SnapGroupObserver> observers_;

  display::ScopedDisplayObserver display_observer_{this};
};

}  // namespace ash

#endif  // ASH_WM_SNAP_GROUP_SNAP_GROUP_CONTROLLER_H_