blob: c73da02edafd9638d88c2e8a1193e40cc28f1a0d [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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_TAB_STRIP_LAYOUT_HELPER_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_LAYOUT_HELPER_H_
#include <vector>
#include "base/callback.h"
#include "base/callback_forward.h"
#include "chrome/browser/ui/tabs/tab_types.h"
#include "chrome/browser/ui/views/tabs/tab_animation_state.h"
#include "chrome/browser/ui/views/tabs/tab_slot_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip_layout.h"
#include "chrome/browser/ui/views/tabs/tab_width_constraints.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/view_model.h"
class Tab;
class TabGroupHeader;
class TabStripController;
namespace tab_groups {
class TabGroupId;
}
// Helper class for TabStrip, that is responsible for calculating and assigning
// layouts for tabs and group headers. It tracks animations and changes to the
// model so that it has all necessary information for layout purposes.
class TabStripLayoutHelper {
public:
using GetTabsCallback = base::RepeatingCallback<views::ViewModelT<Tab>*()>;
TabStripLayoutHelper(const TabStripController* controller,
GetTabsCallback get_tabs_callback);
TabStripLayoutHelper(const TabStripLayoutHelper&) = delete;
TabStripLayoutHelper& operator=(const TabStripLayoutHelper&) = delete;
~TabStripLayoutHelper();
// Returns a vector of all tabs in the strip, including both closing tabs
// and tabs still in the model.
std::vector<Tab*> GetTabs() const;
// Get all tab slot views in visual order, including all tabs from
// GetTabs() and all tab group headers.
std::vector<TabSlotView*> GetTabSlotViews() const;
int active_tab_width() { return active_tab_width_; }
int inactive_tab_width() { return inactive_tab_width_; }
int first_non_pinned_tab_index() { return first_non_pinned_tab_index_; }
int first_non_pinned_tab_x() { return first_non_pinned_tab_x_; }
// Returns the number of pinned tabs in the tabstrip.
int GetPinnedTabCount() const;
// Returns a map of all tab groups and their bounds.
const std::map<tab_groups::TabGroupId, gfx::Rect>& group_header_ideal_bounds()
const {
return group_header_ideal_bounds_;
}
// Inserts a new tab at |index|.
void InsertTabAt(int model_index, Tab* tab, TabPinned pinned);
// Marks the tab at |model_index| as closing, but does not remove it from
// |slots_|.
void RemoveTabAt(int model_index, Tab* tab);
// Called when the tabstrip enters tab closing mode, wherein tabs should
// resize differently to control which tab ends up under the cursor.
// Assumes that the available width will never be smaller than this value
// for the duration of this tab closing session, i.e. that resizing the
// tabstrip will only happen after ExitTabClosingMode().
void EnterTabClosingMode(int available_width);
// Called when the tabstrip has left tab closing mode or when falling back
// to the old animation system while in closing mode. Returns the current
// available width.
absl::optional<int> ExitTabClosingMode();
// Invoked when |tab| has been destroyed by TabStrip (i.e. the remove
// animation has completed).
void OnTabDestroyed(Tab* tab);
// Moves the tab at |prev_index| with group |moving_tab_group| to |new_index|.
// Also updates the group header's location if necessary.
void MoveTab(absl::optional<tab_groups::TabGroupId> moving_tab_group,
int prev_index,
int new_index);
// Sets the tab at |index|'s pinned state to |pinned|.
void SetTabPinned(int model_index, TabPinned pinned);
// Inserts a new group header for |group|.
void InsertGroupHeader(tab_groups::TabGroupId group, TabGroupHeader* header);
// Removes the group header for |group|.
void RemoveGroupHeader(tab_groups::TabGroupId group);
// Ensures the group header for |group| is at the correct index. Should be
// called externally when group membership changes but nothing else about the
// layout does.
void UpdateGroupHeaderIndex(tab_groups::TabGroupId group);
// Changes the active tab from |prev_active_index| to |new_active_index|.
void SetActiveTab(int prev_active_index, int new_active_index);
// Calculates the smallest width the tabs can occupy.
int CalculateMinimumWidth();
// Calculates the width the tabs would occupy if they have enough space.
int CalculatePreferredWidth();
// Generates and sets the ideal bounds for the views in |tabs| and
// |group_headers|. Updates the cached widths in |active_tab_width_| and
// |inactive_tab_width_|. Returns the total width occupied by the new ideal
// bounds.
int UpdateIdealBounds(int available_width);
// Generates and sets the ideal bounds for |tabs|. Updates
// the cached values in |first_non_pinned_tab_index_| and
// |first_non_pinned_tab_x_|.
void UpdateIdealBoundsForPinnedTabs();
private:
struct TabSlot;
// Calculates the bounds each tab should occupy, subject to the provided
// width constraint.
std::vector<gfx::Rect> CalculateIdealBounds(
absl::optional<int> available_width);
// Given |model_index| for a tab already present in |slots_|, return
// the corresponding index in |slots_|.
int GetSlotIndexForExistingTab(int model_index) const;
// For a new tab at |new_model_index|, get the insertion index in
// |slots_|. |group| is the new tab's group.
int GetSlotInsertionIndexForNewTab(
int new_model_index,
absl::optional<tab_groups::TabGroupId> group) const;
// Used internally in the above two functions. For a tabstrip with N
// tabs, this takes 0 <= |model_index| <= N and returns the first
// possible slot corresponding to this model index.
//
// This means that if |model_index| is the first tab in a group, the
// returned slot index will point to the group header. For other tabs,
// the slot index corresponding to that tab will be returned. Finally,
// if |model_index| = N, slots_.size() will be returned.
int GetFirstSlotIndexForTabModelIndex(int model_index) const;
// Given a group ID, returns the index of its header's corresponding TabSlot
// in |slots_|.
int GetSlotIndexForGroupHeader(tab_groups::TabGroupId group) const;
// Returns the current width constraints for each View.
std::vector<TabWidthConstraints> GetCurrentTabWidthConstraints() const;
// Compares |cached_slots_| to the TabAnimations in |animator_| and DCHECKs if
// the TabAnimation::ViewType do not match. Prevents bugs that could cause the
// wrong callback being run when a tab or group is deleted.
void VerifyAnimationsMatchTabSlots() const;
// Updates the value of either |active_tab_width_| or |inactive_tab_width_|,
// as appropriate.
void UpdateCachedTabWidth(int tab_index, int tab_width, bool active);
// The tabstrip may enter 'closing mode' when tabs are closed with the mouse.
// In closing mode, the ideal widths of tabs are manipulated to control which
// tab ends up under the cursor after each remove animation completes - the
// next tab to the right, if it exists, or the next tab to the left otherwise.
// Returns true if any width constraint is currently being enforced.
bool WidthsConstrainedForClosingMode();
// True iff the slot at index |i| is a tab that is in a collapsed group.
bool SlotIsCollapsedTab(int i) const;
// The owning tabstrip's controller.
const TabStripController* const controller_;
// Callback to get the necessary View objects from the owning tabstrip.
GetTabsCallback get_tabs_callback_;
// Current collation of tabs and group headers, along with necessary data to
// run layout and animations for those Views.
std::vector<TabSlot> slots_;
// Contains the ideal bounds of tab group headers.
std::map<tab_groups::TabGroupId, gfx::Rect> group_header_ideal_bounds_;
// When in tab closing mode, if we want the next tab to the right to end up
// under the cursor, each tab needs to stay the same size. When defined,
// this specifies that size.
absl::optional<TabWidthOverride> tab_width_override_;
// When in tab closing mode, if we want the next tab to the left to end up
// under the cursor, the overall space taken by tabs needs to stay the same.
// When defined, this specifies that size.
absl::optional<int> tabstrip_width_override_;
// The current widths of tabs. If the space for tabs is not evenly divisible
// into these widths, the initial tabs in the strip will be 1 px larger.
int active_tab_width_;
int inactive_tab_width_;
int first_non_pinned_tab_index_;
int first_non_pinned_tab_x_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_LAYOUT_HELPER_H_