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
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288

ash / wm / overview / overview_window_drag_controller.h [blame]

// Copyright 2017 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_WINDOW_DRAG_CONTROLLER_H_
#define ASH_WM_OVERVIEW_OVERVIEW_WINDOW_DRAG_CONTROLLER_H_

#include <memory>

#include "ash/ash_export.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/splitview/split_view_types.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size_f.h"

namespace aura {
class Window;
}  // namespace aura

namespace ui {
class PresentationTimeRecorder;
}  // namespace ui

namespace ash {

class OverviewGrid;
class OverviewItemBase;
class OverviewSession;
class SplitViewController;

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Workflows of dragging windows from overview (not from the top or shelf).
enum class OverviewDragAction {
  kToGridSameDisplayClamshellMouse = 0,
  kToGridSameDisplayClamshellTouch = 1,
  kToDeskSameDisplayClamshellMouse = 2,
  kToDeskSameDisplayClamshellTouch = 3,
  kToSnapSameDisplayClamshellMouse = 4,
  kToSnapSameDisplayClamshellTouch = 5,
  kSwipeToCloseSuccessfulClamshellTouch = 6,
  kSwipeToCloseCanceledClamshellTouch = 7,
  kFlingToCloseClamshellTouch = 8,
  kToGridOtherDisplayClamshellMouse = 9,
  kToDeskOtherDisplayClamshellMouse = 10,
  kToSnapOtherDisplayClamshellMouse = 11,
  kToGridSameDisplayTabletTouch = 12,
  kToDeskSameDisplayTabletTouch = 13,
  kToSnapSameDisplayTabletTouch = 14,
  kSwipeToCloseSuccessfulTabletTouch = 15,
  kSwipeToCloseCanceledTabletTouch = 16,
  kFlingToCloseTabletTouch = 17,
  kMaxValue = kFlingToCloseTabletTouch,
};

// The drag controller for an overview window item in overview mode. It updates
// the position of the corresponding window item using transform while dragging.
// It also updates the split view drag indicators, which handles showing
// indicators where to drag, and preview areas showing the bounds of the
// window about to be snapped.
class ASH_EXPORT OverviewWindowDragController {
 public:
  enum class DragBehavior {
    // No drag has started.
    kNoDrag,
    // Drag has started, but it is undecided whether we want to drag to snap or
    // drag to close yet.
    kUndefined,
    // On drag complete, the window will be snapped, if it meets requirements,
    // or moved to another desk if dropped on one of the desks' mini_views. This
    // mode is triggered if the the window is initially dragged horizontally
    // more than vertically (more in X than Y), or if the window item in the
    // overview grid was gesture long pressed.
    kNormalDrag,
    // On drag complete, the window will be closed, if it meets requirements.
    // This mode is triggered when the window is initially dragged vertically
    // more than horizontally (more in Y than in X).
    kDragToClose,
  };

  enum class DragResult {
    // The drag ended without ever being disambiguated between a normal drag and
    // drag-to-close.
    kNeverDisambiguated,
    // The drag was considered as a normal drag, and then the window was dropped
    // back into overview, in the same grid or another one.
    kDropIntoOverview,
    // The drag resulted in snapping the window.
    kSnap,
    // The drag resulted in moving the window to another desk.
    kDragToDesk,
    // The drag resulted in closing the window.
    kSuccessfulDragToClose,
    // The drag was considered as drag-to-close, but did not result in closing
    // the window.
    kCanceledDragToClose,
  };

  OverviewWindowDragController(OverviewSession* overview_session,
                               OverviewItemBase* item,
                               bool is_touch_dragging,
                               OverviewItemBase* event_source_item);

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

  ~OverviewWindowDragController();

  static base::AutoReset<bool> SkipNewDeskButtonScaleUpDurationForTesting();

  OverviewItemBase* item() { return item_; }

  bool is_touch_dragging() const { return is_touch_dragging_; }

  void InitiateDrag(const gfx::PointF& location_in_screen);
  void Drag(const gfx::PointF& location_in_screen);
  DragResult CompleteDrag(const gfx::PointF& location_in_screen);
  void StartNormalDragMode(const gfx::PointF& location_in_screen);
  DragResult Fling(const gfx::PointF& location_in_screen,
                   float velocity_x,
                   float velocity_y);
  void ActivateDraggedWindow();

  // Called when a gesture event is reset or when the dragged window is being
  // destroyed.
  void ResetGesture();

  // Resets |overview_session_| to nullptr. It's needed since we defer the
  // deletion of OverviewWindowDragController in Overview destructor and
  // we need to reset |overview_session_| to nullptr to avoid null pointer
  // dereference.
  void ResetOverviewSession();

  DragBehavior current_drag_behavior_for_testing() const {
    return current_drag_behavior_;
  }

  base::OneShotTimer* new_desk_button_scale_up_timer_for_test() {
    return &new_desk_button_scale_up_timer_;
  }

 private:
  enum NormalDragAction {
    kToGrid = 0,
    kToDesk = 1,
    kToSnap = 2,
    kNormalDragActionEnumSize = 3,
  };
  enum DragToCloseAction {
    kSwipeToCloseSuccessful = 0,
    kSwipeToCloseCanceled = 1,
    kFlingToClose = 2,
    kDragToCloseActionEnumSize = 3,
  };

  void StartDragToCloseMode();

  // Methods to continue and complete the drag when the drag mode is
  // kDragToClose.
  void ContinueDragToClose(const gfx::PointF& location_in_screen);
  DragResult CompleteDragToClose(const gfx::PointF& location_in_screen);

  // Methods to continue and complete the drag when the drag mode is
  // kNormalDrag.
  void ContinueNormalDrag(const gfx::PointF& location_in_screen);
  DragResult CompleteNormalDrag(const gfx::PointF& location_in_screen);

  // Updates visuals for the user while dragging items around.
  void UpdateDragIndicatorsAndOverviewGrid(
      const gfx::PointF& location_in_screen);

  aura::Window* GetRootWindowBeingDraggedIn() const;

  SnapPosition GetSnapPosition(const gfx::PointF& location_in_screen) const;

  // Snaps and activates the window. Uses the divider spawn animation (see
  // |SplitViewController::SnapWindow|). Sets |item_| to null because the
  // overview item is destroyed.
  void SnapWindow(SplitViewController* split_view_controller,
                  SnapPosition snap_position);

  // Returns the item's overview grid, or the grid in which the item is being
  // dragged if the multi display overview and split view feature is enabled.
  OverviewGrid* GetCurrentGrid() const;

  // Records the histogram Ash.Overview.WindowDrag.Workflow.
  void RecordNormalDrag(NormalDragAction action,
                        bool is_dragged_to_other_display) const;
  void RecordDragToClose(DragToCloseAction action) const;

  // Creates `float_drag_helper_` if needed. The helper will temporarily stack
  // the float container under the active desk container, so that dragging
  // regular windows appear above overview items of floated windows.
  void MaybeCreateFloatDragHelper();

  // Scale up the new desk button on the desks bar from expanded state to active
  // state to make the new desk button a drop target for the window being
  // dragged. It's triggered by `new_desk_button_scale_up_timer_`. Refer to
  // `new_desk_button_scale_up_timer_` for more information.
  void MaybeScaleUpNewDeskButton();

  raw_ptr<OverviewSession> overview_session_;

  // The drag target item in the overview mode.
  raw_ptr<OverviewItemBase, DanglingUntriaged> item_ = nullptr;

  // The source item of the drag event.
  raw_ptr<OverviewItemBase, DanglingUntriaged> event_source_item_ = nullptr;

  DragBehavior current_drag_behavior_ = DragBehavior::kNoDrag;

  // The location of the initial mouse/touch/gesture event in screen.
  gfx::PointF initial_event_location_;

  // Stores the bounds of |item_| when a drag is started. Used to calculate the
  // new bounds on a drag event.
  gfx::PointF initial_centerpoint_;

  // The original size of the dragged item after we scale it up when we start
  // dragging it. The item is restored to this size once it no longer intersects
  // with the OverviewDeskBarView.
  gfx::SizeF original_scaled_size_;

  // Track the per-overview-grid desks bar data used to perform the window
  // sizing operations when it is moved towards or on the desks bar.
  struct GridDesksBarData {
    // The scaled-down size of the dragged item once the drag location is on the
    // OverviewDeskBarView of the corresponding grid. We size the item down so
    // that it fits inside the desks' preview view.
    gfx::SizeF on_desks_bar_item_size;

    // Cached values related to dragging items while the desks bar is shown.
    // |desks_bar_bounds| is the bounds of the desks bar in screen coordinates.
    // |shrink_bounds| is a rectangle around the desks bar which the items
    // starts shrinking when the event location is contained. The item will
    // shrink until it is contained in |desks_bar_bounds|, at which it has
    // reached its minimum size and will no longer shrink.
    // |shrink_region_distance| is a vector contained the distance from the
    // origin of |desks_bar_bounds| to the origin of |shrink_bounds|. It's
    // used to determine the size of the dragged item when it's within
    // |shrink_bounds|.
    gfx::RectF desks_bar_bounds;
    gfx::RectF shrink_bounds;
    gfx::Vector2dF shrink_region_distance;
  };
  base::flat_map<OverviewGrid*, GridDesksBarData> per_grid_desks_bar_data_;

  const size_t display_count_;

  // Indicates touch dragging, as opposed to mouse dragging. The drag-to-close
  // mode is only allowed when |is_touch_dragging_| is true.
  const bool is_touch_dragging_;

  // True if the `item_` can be snapped by dragging.
  const bool is_eligible_for_drag_to_snap_;

  // True if the Virtual Desks bar is created and dragging to desks is enabled.
  const bool virtual_desks_bar_enabled_;

  // The opacity of |item_| changes if we are in drag to close mode. Store the
  // original opacity of |item_| and restore it to the item when we leave drag
  // to close mode.
  float original_opacity_ = 1.f;

  // Set to true once the bounds of |item_| change.
  bool did_move_ = false;

  // Records the presentation time of window drag operation in overview mode.
  std::unique_ptr<ui::PresentationTimeRecorder> presentation_time_recorder_;

  SnapPosition snap_position_ = SnapPosition::kNone;

  std::optional<OverviewController::ScopedOcclusionPauser> occlusion_pauser_;

  // A timer used to scale up the new desk button to make it a drop target for
  // the window being dragged if the window is hovered on the button over a
  // period of time.
  base::OneShotTimer new_desk_button_scale_up_timer_;
};

}  // namespace ash

#endif  // ASH_WM_OVERVIEW_OVERVIEW_WINDOW_DRAG_CONTROLLER_H_