blob: b9bd6616746b536f60b177aa379be236f9dd3229 [file] [log] [blame]
// Copyright 2022 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_COMPOUND_TAB_CONTAINER_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_COMPOUND_TAB_CONTAINER_H_
#include <memory>
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/views/tabs/tab_container.h"
#include "chrome/browser/ui/views/tabs/tab_container_controller.h"
#include "components/tab_groups/tab_group_id.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/gfx/geometry/point.h"
#include "ui/views/view_targeter_delegate.h"
class TabHoverCardController;
class TabDragContextBase;
// Composes two TabContainers into one, keeping the pinned tabs in one container
// and the unpinned tabs in the other.
// Indices in the public and private API are all in overall indices, unless
// specifically noted otherwise as being relative to a specific container.
class CompoundTabContainer : public TabContainer,
public views::ViewTargeterDelegate {
METADATA_HEADER(CompoundTabContainer, TabContainer)
public:
CompoundTabContainer(TabContainerController& controller,
TabHoverCardController* hover_card_controller,
TabDragContextBase* drag_context,
TabSlotController& tab_slot_controller,
views::View* scroll_contents_view);
~CompoundTabContainer() override;
// TabContainer:
void SetAvailableWidthCallback(
base::RepeatingCallback<int()> available_width_callback) override;
std::vector<Tab*> AddTabs(
std::vector<TabInsertionParams> tabs_params) override;
void MoveTab(int from_model_index, int to_model_index) override;
void RemoveTab(int index, bool was_active) override;
void SetTabPinned(int model_index, TabPinned pinned) override;
void SetActiveTab(std::optional<size_t> prev_active_index,
std::optional<size_t> new_active_index) override;
Tab* RemoveTabFromViewModel(int model_index) override;
Tab* AddTabToViewModel(Tab* tab, int model_index, TabPinned pinned) override;
void ReturnTabSlotView(TabSlotView* view) override;
void ScrollTabToVisible(int model_index) override;
void ScrollTabContainerByOffset(int offset) override;
void OnGroupCreated(const tab_groups::TabGroupId& group) override;
void OnGroupEditorOpened(const tab_groups::TabGroupId& group) override;
void OnGroupMoved(const tab_groups::TabGroupId& group) override;
void OnGroupContentsChanged(const tab_groups::TabGroupId& group) override;
void OnGroupVisualsChanged(
const tab_groups::TabGroupId& group,
const tab_groups::TabGroupVisualData* old_visuals,
const tab_groups::TabGroupVisualData* new_visuals) override;
void OnGroupClosed(const tab_groups::TabGroupId& group) override;
void ToggleTabGroup(const tab_groups::TabGroupId& group,
bool is_collapsing,
ToggleTabGroupCollapsedStateOrigin origin) override;
void UpdateTabGroupVisuals(tab_groups::TabGroupId group_id) override;
void NotifyTabstripBubbleOpened() override;
void NotifyTabstripBubbleClosed() override;
void OnSplitCreated(const std::vector<int>& indices) override;
void OnSplitRemoved(const std::vector<int>& indices) override;
void OnSplitContentsChanged(const std::vector<int>& indices) override;
std::optional<int> GetModelIndexOf(
const TabSlotView* slot_view) const override;
Tab* GetTabAtModelIndex(int index) const override;
int GetTabCount() const override;
std::optional<int> GetModelIndexOfFirstNonClosingTab(Tab* tab) const override;
void UpdateHoverCard(
Tab* tab,
TabSlotController::HoverCardUpdateType update_type) override;
void HandleLongTap(ui::GestureEvent* event) override;
bool IsRectInContentArea(const gfx::Rect& rect) override;
std::optional<ZOrderableTabContainerElement> GetLeadingElementForZOrdering()
const override;
std::optional<ZOrderableTabContainerElement> GetTrailingElementForZOrdering()
const override;
void OnTabSlotAnimationProgressed(TabSlotView* view) override;
void OnTabCloseAnimationCompleted(Tab* tab) override;
void InvalidateIdealBounds() override;
void AnimateToIdealBounds() override;
bool IsAnimating() const override;
void CancelAnimation() override;
void CompleteAnimationAndLayout() override;
int GetAvailableWidthForTabContainer() const override;
void EnterTabClosingMode(std::optional<int> override_width,
CloseTabSource source) override;
void ExitTabClosingMode() override;
void SetTabSlotVisibility() override;
bool InTabClose() override;
TabGroupViews* GetGroupViews(tab_groups::TabGroupId group_id) const override;
std::map<tab_groups::TabGroupId, TabGroupHeader*> GetGroupHeaders()
const override;
const std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>&
get_group_views_for_testing() const override;
gfx::Rect GetIdealBounds(int model_index) const override;
gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override;
// views::View
gfx::Size GetMinimumSize() const override;
views::SizeBounds GetAvailableSize(const View* child) const override;
gfx::Size CalculatePreferredSize(
const views::SizeBounds& available_size) const override;
views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
void Layout(PassKey) override;
void PaintChildren(const views::PaintInfo& paint_info) override;
void ChildPreferredSizeChanged(views::View* child) override;
// BrowserRootView::DropTarget:
std::optional<BrowserRootView::DropIndex> GetDropIndex(
const ui::DropTargetEvent& event) override;
BrowserRootView::DropTarget* GetDropTarget(
gfx::Point loc_in_local_coords) override;
views::View* GetViewForDrop() override;
void HandleDragUpdate(
const std::optional<BrowserRootView::DropIndex>& index) override;
void HandleDragExited() override;
// views::ViewTargeterDelegate:
views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
// Notifies this CompoundTabContainer that `tab_slot_view` must be animated to
// `target_bounds`. `pinned` indicates whether these bounds are relative to
// `pinned_tab_container_` or `unpinned_tab_container_`.
void UpdateAnimationTarget(TabSlotView* tab_slot_view,
gfx::Rect target_bounds,
TabPinned pinned);
private:
int NumPinnedTabs() const;
// Returns true iff `index` is a valid index in the joint viewmodel index
// space of the two TabContainers, i.e. if there's a Tab view that corresponds
// to `index`. These index spaces are different when the model has added or
// removed a tab but we haven't finished processing that change ourselves.
bool IsValidViewModelIndex(int index) const;
// Moves the tab at `from_model_index` from whichever TabContainer currently
// holds it into the other TabContainer, inserting it into that container at
// the index that corresponds to `to_model_index`.
void TransferTabBetweenContainers(int from_model_index, int to_model_index);
// Converts `ideal_bounds` from `unpinned_tab_container_`'s coordinate space
// into local coordinate space. References ideal bounds instead of current
// bounds to correctly account for any ongoing animations in the pinned tab
// container.
gfx::Rect ConvertUnpinnedContainerIdealBoundsToLocal(
gfx::Rect ideal_bounds) const;
// Animates `tab` to `ideal_bounds` using `bounds_animator_`. Retargets an
// existing animation if one is already running.
void AnimateTabTo(Tab* tab, gfx::Rect ideal_bounds);
// Returns the child TabContainer that should contain `view`. NB this can be
// different from `view->parent()` e.g. while `view` is being dragged.
TabContainer& GetTabContainerFor(TabSlotView* view) const;
// Returns the child TabContainer that should handle text drag and drop events
// at `point_in_local_coords`.
TabContainer* GetTabContainerForDrop(gfx::Point point_in_local_coords) const;
// Returns the child TabContainer that contains `point_in_local_coords`, or
// nullptr if neither contain it. If both contain it, chooses based on where
// `point_in_local_coords` is within the overlap area.
TabContainer* GetTabContainerAt(gfx::Point point_in_local_coords) const;
// Returns the x position that `unpinned_tab_container_` should be at after
// any running animations finish.
int GetUnpinnedContainerIdealLeadingX() const;
int GetAvailableWidthForUnpinnedTabContainer() const;
// Computes the size of this compound container assuming the pinned and
// unpinned containers have the given sizes.
gfx::Size GetCombinedSizeForTabContainerSizes(gfx::Size pinned_size,
gfx::Size unpinned_size) const;
// Private getter to retrieve the visible rect of the scroll container.
std::optional<gfx::Rect> GetVisibleContentRect() const;
// Animates and scrolls the tab container from the start_edge to the
// target_edge. If the target_edge is beyond the tab strip it will be clamped
// bounds of the tabstrip.
void AnimateScrollToShowXCoordinate(const int start_edge,
const int target_edge);
// Forwards the split changed event to the appropriate tab container.
using SplitChangedCallback = void (TabContainer::*)(const std::vector<int>&);
void OnSplitChanged(const std::vector<int>& indices,
SplitChangedCallback callback);
const raw_ref<TabContainerController> controller_;
// Adapts `pinned_tab_container_`'s interactions with the model to account for
// it only holding pinned tabs.
const std::unique_ptr<TabContainerController>
pinned_tab_container_controller_;
// The TabContainer that holds the pinned tabs.
const raw_ref<TabContainer, DanglingUntriaged> pinned_tab_container_;
// Adapts `unpinned_tab_container_`'s interactions with the model to account
// for it only holding unpinned tabs.
const std::unique_ptr<TabContainerController>
unpinned_tab_container_controller_;
// The TabContainer that holds the unpinned tabs.
const raw_ref<TabContainer, DanglingUntriaged> unpinned_tab_container_;
base::RepeatingCallback<int()> available_width_callback_;
const raw_ptr<TabHoverCardController, DanglingUntriaged>
hover_card_controller_;
// The View that is to be scrolled by `tab_scrolling_animation_`. May be
// nullptr in tests.
const raw_ptr<views::View> scroll_contents_view_;
// Responsible for animating the scroll of the tab container.
std::unique_ptr<gfx::LinearAnimation> tab_scrolling_animation_;
// Animates tabs between pinned and unpinned states.
views::BoundsAnimator bounds_animator_;
// The sub-container that handled the last drag/drop update, if any. Used to
// ensure HandleDragExited is called when necessary.
raw_ptr<TabContainer, DanglingUntriaged> current_text_drop_target_ = nullptr;
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_COMPOUND_TAB_CONTAINER_H_