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

ash / drag_drop / drag_drop_controller.h [blame]

// Copyright 2012 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_DRAG_DROP_DRAG_DROP_CONTROLLER_H_
#define ASH_DRAG_DROP_DRAG_DROP_CONTROLLER_H_

#include <memory>
#include <optional>

#include "ash/ash_export.h"
#include "ash/drag_drop/drag_drop_capture_delegate.h"
#include "ash/drag_drop/tab_drag_drop_delegate.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/window_observer.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/widget/unique_widget_ptr.h"

namespace gfx {
class LinearAnimation;
}

namespace ui {
class LocatedEvent;
}

namespace ash {
class ToplevelWindowDragDelegate;

class ASH_EXPORT DragDropController : public aura::client::DragDropClient,
                                      public ui::EventHandler,
                                      public gfx::AnimationDelegate,
                                      public aura::WindowObserver,
                                      public display::DisplayManagerObserver {
 public:
  DragDropController();

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

  ~DragDropController() override;

  void set_enabled(bool enabled) { enabled_ = enabled; }

  void set_toplevel_window_drag_delegate(ToplevelWindowDragDelegate* delegate) {
    toplevel_window_drag_delegate_ = delegate;
  }

  // Returns if the drag drop operation has been fully completed.  This is
  // similar to IsDragDropInProgress, but returns true even after the drop_data
  // is passed to the target, and keep returning true until the drag drop states
  // are callbacks are called), so that the callback receive the proper
  // state.
  bool IsDragDropCompleted();

  // Overridden from aura::client::DragDropClient:
  ui::mojom::DragOperation StartDragAndDrop(
      std::unique_ptr<ui::OSExchangeData> data,
      aura::Window* root_window,
      aura::Window* source_window,
      const gfx::Point& screen_location,
      int allowed_operations,
      ui::mojom::DragEventSource source) override;
  void DragCancel() override;
  bool IsDragDropInProgress() override;
  void AddObserver(aura::client::DragDropClientObserver* observer) override;
  void RemoveObserver(aura::client::DragDropClientObserver* observer) override;

  // Overridden from ui::EventHandler:
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;
  void OnGestureEvent(ui::GestureEvent* event) override;

  // Overridden from aura::WindowObserver.
  void OnWindowDestroying(aura::Window* window) override;

  void SetDragImage(const gfx::ImageSkia& image,
                    const gfx::Vector2d& image_offset);

  ui::mojom::DragEventSource event_source() {
    return current_drag_event_source_;
  }

  // Sets the `closure` that will be executed as a replacement of
  // inner event loop. A test can use this closure to generate events, or
  // take other actions that should happen during the drag and drop, and
  // can also check the condition that should be satisfied.
  // The loop closure is called with a boolean value that indicates
  // that this is called from the inner loop because the same closure will
  // often used to generate the event that will eventually enter the drag
  // and drop inner loop. The `quit_closure` is used for a test
  // to exit the outer loop in the test.
  using TestLoopClosure = base::RepeatingCallback<void()>;
  void SetLoopClosureForTesting(TestLoopClosure closure,
                                base::OnceClosure quit_closure);

  void SetDisableNestedLoopForTesting(bool disable);

  // Deprecated: Use `SetDisableNestedLoopForTesting`.
  void set_should_block_during_drag_drop(bool should_block_during_drag_drop) {
    SetDisableNestedLoopForTesting(!should_block_during_drag_drop);
  }

  void enable_no_image_touch_drag_for_test() {
    allow_no_image_touch_drag_for_test_ = true;
  }

 protected:
  // Helper method to create a LinearAnimation object that will run the drag
  // cancel animation. Caller take ownership of the returned object. Protected
  // for testing.
  virtual gfx::LinearAnimation* CreateCancelAnimation(
      base::TimeDelta duration,
      int frame_rate,
      gfx::AnimationDelegate* delegate);

  // Exposed for tests to override.
  virtual void DragUpdate(aura::Window* target, const ui::LocatedEvent& event);
  virtual void Drop(aura::Window* target, const ui::LocatedEvent& event);

  // Actual implementation of |DragCancel()|. protected for testing.
  virtual void DoDragCancel(base::TimeDelta drag_cancel_animation_duration);

  // Exposed for test assertions.
  DragDropCaptureDelegate* get_capture_delegate() { return capture_delegate_; }

 private:
  friend class DragDropControllerTest;
  friend class DragDropControllerTestApi;

  // Overridden from gfx::AnimationDelegate:
  void AnimationEnded(const gfx::Animation* animation) override;
  void AnimationProgressed(const gfx::Animation* animation) override;
  void AnimationCanceled(const gfx::Animation* animation) override;

  // display::DisplayManagerObserver
  void OnWillApplyDisplayChanges() override;

  // Helper method to start drag widget flying back animation.
  void StartCanceledAnimation(base::TimeDelta animation_duration);

  // Helper methods to forward |pending_log_tap_| event to
  // |drag_source_window_|.
  void ScheduleForwardPendingLongTap();
  void ForwardPendingLongTap();

  // Helper method to reset most of the state, except state that could be used
  // during async operations of cancellation (including cancel animation and
  // posting task to dispatch long tap event).
  void Cleanup();

  void CleanupPendingLongTap();

  // Performs data drop. NOTE: this method does not run in an async drop if
  // disallowed by `ui::DataTransferPolicyController`. `cancel_drag_callback`
  // runs if this method does not run.
  void PerformDrop(const gfx::Point drop_location_in_screen,
                   ui::DropTargetEvent event,
                   std::unique_ptr<ui::OSExchangeData> drag_data,
                   aura::client::DragDropDelegate::DropCallback drop_cb,
                   std::unique_ptr<TabDragDropDelegate> tab_drag_drop_delegate,
                   base::ScopedClosureRunner cancel_drag_callback);

  void CancelIfInProgress();

  bool enabled_ = false;
  bool drag_drop_completed_ = true;
  std::unique_ptr<views::Widget> drag_image_widget_;
  gfx::Vector2d drag_image_offset_;
  std::unique_ptr<ui::OSExchangeData> drag_data_;
  int allowed_operations_ = 0;
  ui::mojom::DragOperation operation_ = ui::mojom::DragOperation::kNone;
  aura::client::DragUpdateInfo current_drag_info_;

  // Used when processing a Chrome tab drag from a WebUI tab strip.
  std::unique_ptr<TabDragDropDelegate> tab_drag_drop_delegate_;

  // Used when processing a normal drag and drop with touch.
  std::unique_ptr<DragDropCaptureDelegate> touch_drag_drop_delegate_;

  // Window that is currently under the drag cursor.
  raw_ptr<aura::Window> drag_window_ = nullptr;

  // Starting and final bounds for the drag image for the drag cancel animation.
  gfx::Rect drag_image_initial_bounds_for_cancel_animation_;
  gfx::Rect drag_image_final_bounds_for_cancel_animation_;

  std::unique_ptr<gfx::LinearAnimation> cancel_animation_;
  std::unique_ptr<gfx::AnimationDelegate> cancel_animation_notifier_;

  // Window that started the drag.
  raw_ptr<aura::Window> drag_source_window_ = nullptr;

  // A closure that allows a test to implement the actions within
  // drag and drop event loop.
  TestLoopClosure test_loop_closure_;

  // True if the nested event loop is disabled.
  bool nested_loop_disabled_for_testing_ = false;

  // Closure for quitting nested run loop.
  base::OnceClosure quit_closure_;

  // If non-null, a drag is active which required a capture window.
  raw_ptr<DragDropCaptureDelegate, DanglingUntriaged> capture_delegate_ =
      nullptr;

  ui::mojom::DragEventSource current_drag_event_source_ =
      ui::mojom::DragEventSource::kMouse;

  // Holds a synthetic long tap event to be sent to the |drag_source_window_|.
  // See comment in OnGestureEvent() on why we need this.
  std::unique_ptr<ui::Event> pending_long_tap_;
  // Set to true during async operations of cancellation (including cancel
  // animation and posting task to dispatch long tap event), indicating that a
  // long tap event will be dispatched.
  bool will_forward_long_tap_ = false;

  gfx::Point start_location_;
  gfx::Point current_location_;

  base::ObserverList<aura::client::DragDropClientObserver>::
      UncheckedAndDanglingUntriaged observers_;

  raw_ptr<ToplevelWindowDragDelegate, DanglingUntriaged>
      toplevel_window_drag_delegate_ = nullptr;

  bool allow_no_image_touch_drag_for_test_ = false;

  // Weak ptr for async callbacks to be invalidated if a new drag starts.
  base::WeakPtrFactory<DragDropController> weak_factory_{this};
};

}  // namespace ash

#endif  // ASH_DRAG_DROP_DRAG_DROP_CONTROLLER_H_