chromium/ash/drag_drop/tab_drag_drop_delegate.h

// Copyright 2020 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_TAB_DRAG_DROP_DELEGATE_H_
#define ASH_DRAG_DROP_TAB_DRAG_DROP_DELEGATE_H_

#include <memory>

#include "ash/ash_export.h"
#include "ash/drag_drop/drag_drop_capture_delegate.h"
#include "ash/drag_drop/tab_drag_drop_windows_hider.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_types.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "ui/aura/window_observer.h"

namespace aura {
class Window;
}

namespace ui {
class OSExchangeData;
class Point;
class PresentationTimeRecorder;
}  // namespace ui

namespace ash {

class SplitViewDragIndicators;
class TabDragDropWindowsHider;

// Provides special handling for Chrome tab drags on behalf of
// DragDropController. This must be created at the beginning of a tab drag and
// destroyed at the end.
class ASH_EXPORT TabDragDropDelegate : public DragDropCaptureDelegate,
                                       public aura::WindowObserver {
 public:
  // Determines whether |drag_data| indicates a tab drag from a WebUI tab strip
  // (or simply returns false if the integration is disabled).
  static bool IsChromeTabDrag(const ui::OSExchangeData& drag_data);

  // Returns whether a tab from |window| is actively being dragged.
  static bool IsSourceWindowForDrag(const aura::Window* window);

  // |root_window| is the root window from which the drag originated. The drag
  // is expected to stay in |root_window|. |source_window| is the Chrome window
  // the tab was dragged from. |start_location_in_screen| is the location of
  // the touch or click that started the drag.
  TabDragDropDelegate(aura::Window* root_window,
                      aura::Window* source_window,
                      const gfx::Point& start_location_in_screen);
  ~TabDragDropDelegate() override;

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

  const aura::Window* root_window() const { return root_window_; }

  // Must be called on every drag update.
  void DragUpdate(const gfx::Point& location_in_screen);

  // Must be called on drop if it was not accepted by the drop target. After
  // calling this, this delegate must not be used.
  void DropAndDeleteSelf(const gfx::Point& location_in_screen,
                         const ui::OSExchangeData& drop_data);

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

 private:
  FRIEND_TEST_ALL_PREFIXES(TabDragDropDelegateTest, DropWithoutNewWindow);

  // Scales or transforms the source window if appropriate for this drag.
  // |candidate_snap_position| is where the dragged tab will be snapped
  // if dropped immediately.
  void UpdateSourceWindowBoundsIfNecessary(
      SnapPosition candidate_snap_position,
      const gfx::Point& location_in_screen);

  // Puts the source window back into its original position.
  void RestoreSourceWindowBounds();

  // Effectively handles the new window creation in DropAndDeleteSelf(). This
  // method can be called asynchronously in case of Lacros.
  void OnNewBrowserWindowCreated(const gfx::Point& location_in_screen,
                                 aura::Window* new_window);

  // This method returns true when all of the conditions below are met
  //
  // - Not in split view mode
  // - In landscape mode
  // - The current drag location is inside the WebUI tab strip
  //
  // When it returns true, we don't allow to enter split view because
  // it hinders dragging tabs within the tab strip to trigger auto-scroll.
  // This restriction does not apply to split screen mode because either
  // the left/right could be non browser window, which may lead to
  // confusing behavior.
  // It also does not apply to portrait mode because dragging up/down to
  // enter split screen does not hinder dragging left/right to move tabs.
  //
  // https://crbug.com/1316070
  bool ShouldPreventSnapToTheEdge(const gfx::Point& location_in_screen);

  const raw_ptr<aura::Window> root_window_;
  raw_ptr<aura::Window> source_window_;
  const gfx::Point start_location_in_screen_;

  std::unique_ptr<SplitViewDragIndicators> split_view_drag_indicators_;
  std::unique_ptr<TabDragDropWindowsHider> windows_hider_;

  // Presentation time recorder for tab dragging in tablet mode with webui
  // tab strip enable.
  std::unique_ptr<ui::PresentationTimeRecorder> tab_dragging_recorder_;
};

}  // namespace ash

#endif  // ASH_DRAG_DROP_TAB_DRAG_DROP_DELEGATE_H_