blob: 6db65c1cb260243c4b6aca761f8148e4f62784d2 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_TABS_VERTICAL_VERTICAL_DRAGGED_TABS_CONTAINER_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_VERTICAL_VERTICAL_DRAGGED_TABS_CONTAINER_H_
#include "base/callback_list.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ref.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ui/views/tabs/dragging/tab_drag_target.h"
#include "chrome/browser/ui/views/tabs/vertical/tab_drag_scroll_handler.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/views/view_observer.h"
class TabCollectionNode;
class VerticalTabDragHandler;
namespace views {
class View;
struct ProposedLayout;
class ScrollView;
} // namespace views
// `VerticalDraggedTabsContainer` is an abstract class that can be derived to
// support handling dragged vertical tabs within the vertical tab strip view
// hierarchy. The class implements the `TabDragTarget` interface, allowing it to
// be targeted by the core tab dragging logic. It also supports nested targets,
// allowing the dragged tab view to be reparented into child views of this.
class VerticalDraggedTabsContainer : public TabDragTarget,
public views::ViewObserver,
public gfx::AnimationDelegate {
public:
// The axes that the dragged tabs can move on.
enum class DragAxes { kVerticalOnly, kBoth };
// How the dragged tabs should be laid out.
enum class DragLayout { kVertical, kSquash };
VerticalDraggedTabsContainer(views::View& host_view,
TabCollectionNode* collection_node,
DragAxes drag_axis,
DragLayout drag_layout);
VerticalDraggedTabsContainer(const VerticalDraggedTabsContainer& other) =
delete;
VerticalDraggedTabsContainer& operator=(const VerticalDraggedTabsContainer&) =
delete;
~VerticalDraggedTabsContainer() override;
// Recursively searches through the view hierarchy to find the collection
// that should should be handling the tab drag at the given point.
virtual VerticalDraggedTabsContainer& GetTabDragTarget(
const gfx::Point& point_in_screen);
// TabDragTarget
TabDragContext* OnTabDragUpdated(TabDragTarget::DragController& controller,
const gfx::Point& point_in_screen) override;
void OnTabDragEntered() override {}
void OnTabDragExited(const gfx::Point& point_in_screen) override;
void OnTabDragEnded() override;
bool CanDropTab() final;
void HandleTabDrop(TabDragTarget::DragController& controller) final {}
base::CallbackListSubscription RegisterWillDestroyCallback(
base::OnceClosure callback) final;
// ViewObserver
void OnViewBoundsChanged(views::View* observed_view) override;
// AnimationDelegate
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
// Whether this container is currently handling a drag.
bool IsHandlingDrag() const;
// Returns the bounds of the box containing all dragged views, adjusted to
// the point `point_in_container`. The returned bounds are not clamped to the
// container bounds.
gfx::Rect GetDraggingViewsBoundsAtPoint(
const gfx::Point& point_in_container) const;
// Returns the bounds of the box containing all dragged views, adjusted to the
// last drag point. The returned bounds are not clamped to the container
// bounds.
gfx::Rect GetDraggingViewsBounds() const;
protected:
struct DraggedViewVisualData {
gfx::Vector2d offset;
bool should_hide = false;
// If true, surrounding views do not have to be laid out around this
// view.
bool should_float = false;
};
// Returns the expected visual data, relative to the host view, for a dragged
// view. Returns std::nullopt if the view is not being dragged.
std::optional<DraggedViewVisualData> GetVisualDataForDraggedView(
const views::View& view) const;
// Helper for getting the target view for the given drag bounds, excluding
// dragged views.
views::View* GetViewForDragBounds(const views::ProposedLayout& layout,
const gfx::Rect& dragged_tab_bounds);
// Returns whether the two rects overlap by at least the provided minimums for
// each axis.
// E.g, if only `min_x_overlap` is provided, then this will return true
// iff the range of [a.x(), a.right()] overlaps with the range [b.x(),
// b.right()]. If both axes are specified, then this must be true for each
// respective axis.
bool HasMinimumOverlap(const gfx::Rect& a,
const gfx::Rect& b,
std::optional<int> min_x_overlap,
std::optional<int> min_y_overlap) const;
VerticalTabDragHandler& GetDragHandler();
const VerticalTabDragHandler& GetDragHandler() const;
private:
// Returns the scroll view for the container.
virtual views::ScrollView* GetScrollViewForContainer() const = 0;
// Updates the target layout of the host view, and snaps `views_to_snap` to
// their target layout, skipping animations.
virtual void UpdateTargetLayoutForDrag(
const std::vector<const views::View*>& views_to_snap) = 0;
// Get the layout of the host view, skipping animations.
virtual const views::ProposedLayout& GetLayoutForDrag() const = 0;
// Handles a dragged tab that is parented within this target.
// `point_in_container` is a point relative to this target's view.
virtual void HandleTabDragInContainer(
const gfx::Rect& dragged_tab_bounds) = 0;
// Handles dragged tabs entering this container, applying the necessary
// updates to reparent them into this.
void HandleTabDragEnteredContainer();
// Updates state related to dragging tabs, to be used when this container
// starts handling a drag.
void InitializeDragState(TabDragTarget::DragController& controller);
// Initializes the animation for dragged views to be ordered contiguously.
void InitializeDragStartAnimation(
const TabDragTarget::DragController& controller);
// Builds `dragging_views_` and `dragging_views_bounds_` for the given
// drag data.
void BuildDragLayout(const DragSessionData& drag_data);
void AddViewToVerticalDragLayout(views::View* dragging_view,
const gfx::Rect& view_bounds,
bool is_source_dragged_view);
void AddViewToSquashedDragLayout(views::View* dragging_view,
const gfx::Rect& view_bounds,
bool is_source_dragged_view);
// Whether the tab strip is collapsed.
bool IsTabStripCollapsed() const;
// Clears drag state and removes the transformations that were being used for
// the drag.
void ResetDragState();
// Updates the transformations applied to dragging views, according to
// the last drag point.
void UpdateDraggingViewTransforms(const gfx::Point& point_in_container);
bool IsHorizontalDragSupported() const;
// Returns the bounds of the box containing all dragged views, adjusted to
// the point `point_in_container` and clamped to the bounds of the
// scroll view, which should be used for visual representation of the dragged
// views.
gfx::Rect GetDraggingViewsBoundsAtPointClamped(
const gfx::Point& point_in_container) const;
// Returns the expected position for the dragging view, given position of
// the bounding box for views and a target offset within it.
// This takes animation progress into account.
gfx::Vector2d GetDraggingViewPositionForBounds(
const views::View* dragging_view,
const gfx::Rect& dragging_views_bounding_box,
const gfx::Vector2d& target_offset) const;
void ResetCollectionNode();
// Handles updates, both visually and in the model, for whenever the dragged
// tabs' position changes.
void ApplyUpdatesForDragPositionChange();
std::vector<const views::View*> GetDraggingViews() const;
const raw_ref<views::View> host_view_;
raw_ptr<TabCollectionNode> collection_node_;
base::CallbackListSubscription node_destroyed_subscription_;
int tab_strip_padding_;
gfx::Point last_drag_point_in_screen_;
// The bounding box of all the dragged views, relative to the drag point.
gfx::Rect dragging_views_bounds_;
// Child views that are being dragged, mapped to their DraggedViewVisualData,
// whose offset is relative within `dragging_views_bounds_`.
base::flat_map<raw_ptr<views::View>, DraggedViewVisualData> dragging_views_;
// Dragged views must animate into contiguous/stacked order. This keeps track
// of the starting position, used for animation.
base::flat_map<raw_ptr<views::View>, gfx::Vector2d>
animating_views_start_offsets_;
gfx::SlideAnimation drag_start_animation_;
const DragAxes drag_axes_;
const DragLayout drag_layout_;
base::ScopedObservation<views::View, views::ViewObserver>
host_view_observation_{this};
TabDragScrollHandler scroll_handler_;
std::optional<base::CallbackListSubscription> on_scrolled_subscription_ =
std::nullopt;
base::OnceClosureList on_will_destroy_callback_list_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_VERTICAL_VERTICAL_DRAGGED_TABS_CONTAINER_H_