| // Copyright 2017 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_H_ |
| #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/compiler_specific.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/observer_list.h" |
| #include "base/scoped_observation.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ui/tabs/tab_types.h" |
| #include "chrome/browser/ui/tabs/tab_utils.h" |
| #include "chrome/browser/ui/views/frame/browser_root_view.h" |
| #include "chrome/browser/ui/views/tabs/tab.h" |
| #include "chrome/browser/ui/views/tabs/tab_animation_state.h" |
| #include "chrome/browser/ui/views/tabs/tab_controller.h" |
| #include "chrome/browser/ui/views/tabs/tab_drag_context.h" |
| #include "chrome/browser/ui/views/tabs/tab_group_header.h" |
| #include "chrome/browser/ui/views/tabs/tab_group_views.h" |
| #include "components/tab_groups/tab_group_visual_data.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/base/pointer/touch_ui_controller.h" |
| #include "ui/gfx/color_palette.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/views/animation/bounds_animator.h" |
| #include "ui/views/animation/bounds_animator_observer.h" |
| #include "ui/views/controls/button/image_button.h" |
| #include "ui/views/mouse_watcher.h" |
| #include "ui/views/view.h" |
| #include "ui/views/view_model.h" |
| #include "ui/views/view_targeter_delegate.h" |
| #include "ui/views/widget/widget_observer.h" |
| |
| class StackedTabStripLayout; |
| class Tab; |
| class TabHoverCardController; |
| class TabStripController; |
| class TabStripObserver; |
| class TabStripLayoutHelper; |
| |
| namespace gfx { |
| class Rect; |
| } |
| |
| namespace tab_groups { |
| class TabGroupId; |
| } |
| |
| namespace ui { |
| class ListSelectionModel; |
| } |
| |
| namespace views { |
| class ImageView; |
| } |
| |
| // A View that represents the TabStripModel. The TabStrip has the |
| // following responsibilities: |
| // |
| // - It implements the TabStripModelObserver interface, and acts as a |
| // container for Tabs, and is also responsible for creating them. |
| // |
| // - It takes part in Tab Drag & Drop with Tab, TabDragHelper and |
| // DraggedTab, focusing on tasks that require reshuffling other tabs |
| // in response to dragged tabs. |
| class TabStrip : public views::View, |
| public views::MouseWatcherListener, |
| public views::ViewObserver, |
| public views::ViewTargeterDelegate, |
| public views::WidgetObserver, |
| public views::BoundsAnimatorObserver, |
| public TabController, |
| public BrowserRootView::DropTarget { |
| public: |
| METADATA_HEADER(TabStrip); |
| explicit TabStrip(std::unique_ptr<TabStripController> controller); |
| TabStrip(const TabStrip&) = delete; |
| TabStrip& operator=(const TabStrip&) = delete; |
| ~TabStrip() override; |
| |
| void SetAvailableWidthCallback( |
| base::RepeatingCallback<int()> available_width_callback); |
| |
| void NewTabButtonPressed(const ui::Event& event); |
| |
| // Returns the size needed for the specified views. This is invoked during |
| // drag and drop to calculate offsets and positioning. |
| static int GetSizeNeededForViews(const std::vector<TabSlotView*>& views); |
| |
| // Add and remove observers to changes within this TabStrip. |
| void AddObserver(TabStripObserver* observer); |
| void RemoveObserver(TabStripObserver* observer); |
| |
| // Called when the colors of the frame change. |
| void FrameColorsChanged(); |
| |
| // Sets |background_offset_| and schedules a paint. |
| void SetBackgroundOffset(int background_offset); |
| |
| // Returns true if the specified rect (in TabStrip coordinates) intersects |
| // the window caption area of the browser window. |
| bool IsRectInWindowCaption(const gfx::Rect& rect); |
| |
| // Returns true if the specified point (in TabStrip coordinates) is in the |
| // window caption area of the browser window. |
| bool IsPositionInWindowCaption(const gfx::Point& point); |
| |
| // Returns false when there is a drag operation in progress so that the frame |
| // doesn't close. |
| bool IsTabStripCloseable() const; |
| |
| // Returns true if the tab strip is editable. Returns false if the tab strip |
| // is being dragged or animated to prevent extensions from messing things up |
| // while that's happening. |
| bool IsTabStripEditable() const; |
| |
| // Returns information about tabs at given indices. |
| bool IsTabCrashed(int tab_index) const; |
| bool TabHasNetworkError(int tab_index) const; |
| absl::optional<TabAlertState> GetTabAlertState(int tab_index) const; |
| |
| // Updates the loading animations displayed by tabs in the tabstrip to the |
| // next frame. The |elapsed_time| parameter is shared between tabs and used to |
| // keep the throbbers in sync. |
| void UpdateLoadingAnimations(const base::TimeDelta& elapsed_time); |
| |
| // If |adjust_layout| is true the stacked layout changes based on whether the |
| // user uses a mouse or a touch device with the tabstrip. |
| void set_adjust_layout(bool adjust_layout) { adjust_layout_ = adjust_layout; } |
| |
| // |stacked_layout_| defines what should happen when the tabs won't fit at |
| // their ideal size. When |stacked_layout_| is true the tabs are always sized |
| // to their ideal size and stacked on top of each other so that only a certain |
| // set of tabs are visible. This is used when the user uses a touch device. |
| // When |stacked_layout_| is false the tabs shrink to accommodate the |
| // available space. This is the default. |
| bool stacked_layout() const { return stacked_layout_; } |
| |
| // Sets |stacked_layout_| and animates if necessary. |
| void SetStackedLayout(bool stacked_layout); |
| |
| // Adds a tab at the specified index. |
| void AddTabAt(int model_index, TabRendererData data, bool is_active); |
| |
| // Moves a tab. |
| void MoveTab(int from_model_index, int to_model_index, TabRendererData data); |
| |
| // Removes a tab at the specified index. If the tab with |contents| is being |
| // dragged then the drag is completed. |
| void RemoveTabAt(content::WebContents* contents, |
| int model_index, |
| bool was_active); |
| |
| void ScrollTabToVisible(int model_index); |
| |
| // Sets the tab data at the specified model index. |
| void SetTabData(int model_index, TabRendererData data); |
| |
| // Sets the tab group at the specified model index. |
| void AddTabToGroup(absl::optional<tab_groups::TabGroupId> group, |
| int model_index); |
| |
| // Creates the views associated with a newly-created tab group. |
| void OnGroupCreated(const tab_groups::TabGroupId& group); |
| |
| // Opens the editor bubble for the tab |group| as a result of an explicit user |
| // action to create the |group|. |
| void OnGroupEditorOpened(const tab_groups::TabGroupId& group); |
| |
| // Updates the group's contents and metadata when its tab membership changes. |
| // This should be called when a tab is added to or removed from a group. |
| void OnGroupContentsChanged(const tab_groups::TabGroupId& group); |
| |
| // Updates the group's tabs and header when its associated TabGroupVisualData |
| // changes. This should be called when the result of |
| // |TabStripController::GetGroupTitle(group)| or |
| // |TabStripController::GetGroupColorId(group)| changes. |
| void OnGroupVisualsChanged(const tab_groups::TabGroupId& group, |
| const tab_groups::TabGroupVisualData* old_visuals, |
| const tab_groups::TabGroupVisualData* new_visuals); |
| |
| // Handles animations relating to toggling the collapsed state of a group. |
| void ToggleTabGroup(const tab_groups::TabGroupId& group, |
| bool is_collapsing, |
| ToggleTabGroupCollapsedStateOrigin origin); |
| |
| // Updates the ordering of the group header when the whole group is moved. |
| // Needed to ensure display and focus order of the group header view. |
| void OnGroupMoved(const tab_groups::TabGroupId& group); |
| |
| // Destroys the views associated with a recently deleted tab group. |
| void OnGroupClosed(const tab_groups::TabGroupId& group); |
| |
| // Attempts to move the specified group to the left. |
| void ShiftGroupLeft(const tab_groups::TabGroupId& group); |
| |
| // Attempts to move the specified group to the right. |
| void ShiftGroupRight(const tab_groups::TabGroupId& group); |
| |
| // Returns true if the tab is not partly or fully clipped (due to overflow), |
| // and the tab couldn't become partly clipped due to changing the selected tab |
| // (for example, if currently the strip has the last tab selected, and |
| // changing that to the first tab would cause |tab| to be pushed over enough |
| // to clip). |
| bool ShouldTabBeVisible(const Tab* tab) const; |
| |
| // Returns whether or not strokes should be drawn around and under the tabs. |
| bool ShouldDrawStrokes() const; |
| |
| // Invoked when the selection is updated. |
| void SetSelection(const ui::ListSelectionModel& new_selection); |
| |
| // Invoked when a tab needs to show UI that it needs the user's attention. |
| void SetTabNeedsAttention(int model_index, bool attention); |
| |
| // Retrieves the ideal bounds for the Tab at the specified index. |
| const gfx::Rect& ideal_bounds(int tab_data_index) const { |
| return tabs_.ideal_bounds(tab_data_index); |
| } |
| |
| // Retrieves the ideal bounds for the Tab Group Header at the specified group. |
| const gfx::Rect& ideal_bounds(tab_groups::TabGroupId group) const; |
| |
| // Returns the Tab at |index|. |
| // TODO(pkasting): Make const correct |
| Tab* tab_at(int index) const { return tabs_.view_at(index); } |
| |
| // Returns the TabGroupHeader with ID |id|. |
| TabGroupHeader* group_header(const tab_groups::TabGroupId& id) const { |
| return group_views_.at(id).get()->header(); |
| } |
| |
| // Returns the index of the specified view in the model coordinate system, or |
| // -1 if view is closing or not a tab. |
| int GetModelIndexOf(const TabSlotView* view) const; |
| |
| // Gets the number of Tabs in the tab strip. |
| int GetTabCount() const; |
| |
| // Cover method for TabStripController::GetCount. |
| int GetModelCount() const; |
| |
| // Cover method for TabStripController::IsValidIndex. |
| bool IsValidModelIndex(int model_index) const; |
| |
| TabStripController* controller() const { return controller_.get(); } |
| |
| TabDragContext* GetDragContext(); |
| |
| // Returns the number of pinned tabs. |
| int GetPinnedTabCount() const; |
| |
| // Returns true if Tabs in this TabStrip are currently changing size or |
| // position. |
| bool IsAnimating() const; |
| |
| // Stops any ongoing animations. If |layout| is true and an animation is |
| // ongoing this does a layout. |
| void StopAnimating(bool layout); |
| |
| // Returns the index of the focused tab, if any. |
| absl::optional<int> GetFocusedTabIndex() const; |
| |
| // Returns a view for anchoring an in-product help promo. |index_hint| |
| // indicates at which tab the promo should be displayed, but is not |
| // binding. |
| views::View* GetTabViewForPromoAnchor(int index_hint); |
| |
| // Gets the default focusable child view in the TabStrip. |
| views::View* GetDefaultFocusableChild(); |
| |
| // TabController: |
| const ui::ListSelectionModel& GetSelectionModel() const override; |
| bool SupportsMultipleSelection() override; |
| bool ShouldHideCloseButtonForTab(Tab* tab) const override; |
| void SelectTab(Tab* tab, const ui::Event& event) override; |
| void ExtendSelectionTo(Tab* tab) override; |
| void ToggleSelected(Tab* tab) override; |
| void AddSelectionFromAnchorTo(Tab* tab) override; |
| void CloseTab(Tab* tab, CloseTabSource source) override; |
| void ShiftTabNext(Tab* tab) override; |
| void ShiftTabPrevious(Tab* tab) override; |
| void MoveTabFirst(Tab* tab) override; |
| void MoveTabLast(Tab* tab) override; |
| void ShowContextMenuForTab(Tab* tab, |
| const gfx::Point& p, |
| ui::MenuSourceType source_type) override; |
| bool IsActiveTab(const Tab* tab) const override; |
| bool IsTabSelected(const Tab* tab) const override; |
| bool IsTabPinned(const Tab* tab) const override; |
| bool IsTabFirst(const Tab* tab) const override; |
| bool IsFocusInTabs() const override; |
| void MaybeStartDrag( |
| TabSlotView* source, |
| const ui::LocatedEvent& event, |
| const ui::ListSelectionModel& original_selection) override; |
| void ContinueDrag(views::View* view, const ui::LocatedEvent& event) override; |
| bool EndDrag(EndDragReason reason) override; |
| Tab* GetTabAt(const gfx::Point& point) override; |
| const Tab* GetAdjacentTab(const Tab* tab, int offset) override; |
| void OnMouseEventInTab(views::View* source, |
| const ui::MouseEvent& event) override; |
| void UpdateHoverCard(Tab* tab, HoverCardUpdateType update_type) override; |
| bool ShowDomainInHoverCards() const override; |
| bool HoverCardIsShowingForTab(Tab* tab) override; |
| int GetBackgroundOffset() const override; |
| int GetStrokeThickness() const override; |
| bool CanPaintThrobberToLayer() const override; |
| bool HasVisibleBackgroundTabShapes() const override; |
| bool ShouldPaintAsActiveFrame() const override; |
| SkColor GetToolbarTopSeparatorColor() const override; |
| SkColor GetTabSeparatorColor() const override; |
| SkColor GetTabBackgroundColor( |
| TabActive active, |
| BrowserFrameActiveState active_state) const override; |
| SkColor GetTabForegroundColor(TabActive active, |
| SkColor background_color) const override; |
| std::u16string GetAccessibleTabName(const Tab* tab) const override; |
| absl::optional<int> GetCustomBackgroundId( |
| BrowserFrameActiveState active_state) const override; |
| gfx::Rect GetTabAnimationTargetBounds(const Tab* tab) override; |
| float GetHoverOpacityForTab(float range_parameter) const override; |
| float GetHoverOpacityForRadialHighlight() const override; |
| std::u16string GetGroupTitle( |
| const tab_groups::TabGroupId& group) const override; |
| tab_groups::TabGroupColorId GetGroupColorId( |
| const tab_groups::TabGroupId& group) const override; |
| SkColor GetPaintedGroupColor( |
| const tab_groups::TabGroupColorId& color_id) const override; |
| |
| // MouseWatcherListener: |
| void MouseMovedOutOfHost() override; |
| |
| // views::View: |
| void Layout() override; |
| void PaintChildren(const views::PaintInfo& paint_info) override; |
| gfx::Size GetMinimumSize() const override; |
| gfx::Size CalculatePreferredSize() const override; |
| views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override; |
| |
| // BrowserRootView::DropTarget: |
| BrowserRootView::DropIndex GetDropIndex( |
| const ui::DropTargetEvent& event) override; |
| views::View* GetViewForDrop() override; |
| void HandleDragUpdate( |
| const absl::optional<BrowserRootView::DropIndex>& index) override; |
| void HandleDragExited() override; |
| |
| private: |
| class RemoveTabDelegate; |
| class TabDragContextImpl; |
| |
| friend class TabDragControllerTest; |
| friend class TabDragContextImpl; |
| friend class TabGroupEditorBubbleViewDialogBrowserTest; |
| friend class TabHoverCardBubbleViewBrowserTest; |
| friend class TabHoverCardBubbleViewInteractiveUiTest; |
| friend class TabStripTestBase; |
| friend class TabStripRegionViewTestBase; |
| |
| class TabContextMenuController : public views::ContextMenuController { |
| public: |
| explicit TabContextMenuController(TabStrip* parent); |
| // views::ContextMenuController: |
| void ShowContextMenuForViewImpl(views::View* source, |
| const gfx::Point& point, |
| ui::MenuSourceType source_type) override; |
| |
| private: |
| TabStrip* const parent_; |
| }; |
| |
| // Used during a drop session of a url. Tracks the position of the drop as |
| // well as a window used to highlight where the drop occurs. |
| class DropArrow : public views::WidgetObserver { |
| public: |
| DropArrow(const BrowserRootView::DropIndex& index, |
| bool point_down, |
| views::Widget* context); |
| DropArrow(const DropArrow&) = delete; |
| DropArrow& operator=(const DropArrow&) = delete; |
| ~DropArrow() override; |
| |
| void set_index(const BrowserRootView::DropIndex& index) { index_ = index; } |
| BrowserRootView::DropIndex index() const { return index_; } |
| |
| void SetPointDown(bool down); |
| bool point_down() const { return point_down_; } |
| |
| void SetWindowBounds(const gfx::Rect& bounds); |
| |
| // views::WidgetObserver: |
| void OnWidgetDestroying(views::Widget* widget) override; |
| |
| private: |
| // Index of the tab to drop on. |
| BrowserRootView::DropIndex index_; |
| |
| // Direction the arrow should point in. If true, the arrow is displayed |
| // above the tab and points down. If false, the arrow is displayed beneath |
| // the tab and points up. |
| bool point_down_ = false; |
| |
| // Renders the drop indicator. |
| views::Widget* arrow_window_ = nullptr; |
| |
| views::ImageView* arrow_view_ = nullptr; |
| |
| base::ScopedObservation<views::Widget, views::WidgetObserver> |
| scoped_observation_{this}; |
| }; |
| |
| void Init(); |
| |
| views::ViewModelT<Tab>* tabs_view_model() { return &tabs_; } |
| |
| std::map<tab_groups::TabGroupId, TabGroupHeader*> GetGroupHeaders(); |
| |
| // Invoked from |AddTabAt| after the newly created tab has been inserted. |
| void StartInsertTabAnimation(int model_index, TabPinned pinned); |
| |
| // Animates the removal of the tab at |model_index|. Defers to the old |
| // animation style when appropriate. |
| void StartRemoveTabAnimation(int model_index, bool was_active); |
| |
| // Animates the removal of the tab at |model_index| using the old animation |
| // style. |
| void StartFallbackRemoveTabAnimation(int model_index, bool was_active); |
| |
| // Invoked from |MoveTab| after |tab_data_| has been updated to animate the |
| // move. |
| void StartMoveTabAnimation(); |
| |
| // Animates all the views to their ideal bounds. |
| // NOTE: this does *not* invoke UpdateIdealBounds, it uses the bounds |
| // currently set in ideal_bounds. |
| void AnimateToIdealBounds(); |
| |
| // Teleports the tabs to their ideal bounds. |
| // NOTE: this does *not* invoke UpdateIdealBounds, it uses the bounds |
| // currently set in ideal_bounds. |
| void SnapToIdealBounds(); |
| |
| void ExitTabClosingMode(); |
| |
| // Returns whether the close button should be highlighted after a remove. |
| bool ShouldHighlightCloseButtonAfterRemove(); |
| |
| // Returns whether the window background behind the tabstrip is transparent. |
| bool TitlebarBackgroundIsTransparent() const; |
| |
| // Invoked from Layout if the size changes or layout is really needed. |
| void CompleteAnimationAndLayout(); |
| |
| // Sets the visibility state of all tabs and group headers (if any) based on |
| // ShouldTabBeVisible(). |
| void SetTabSlotVisibility(); |
| |
| // Updates the indexes and count for AX data on all tabs. Used by some screen |
| // readers (e.g. ChromeVox). |
| void UpdateAccessibleTabIndices(); |
| |
| // Returns the current width of the active tab. |
| int GetActiveTabWidth() const; |
| |
| // Returns the current width of inactive tabs. An individual inactive tab may |
| // differ from this width slightly due to rounding. |
| int GetInactiveTabWidth() const; |
| |
| // Returns the last tab in the strip that's actually visible. This will be |
| // the actual last tab unless the strip is in the overflow node_data. |
| const Tab* GetLastVisibleTab() const; |
| |
| // Returns the view index (the order of ChildViews of the TabStrip) of the |
| // given |tab| based on its model index when it moves. Used to reorder the |
| // child views of the tabstrip so that focus order stays consistent. |
| int GetViewInsertionIndex(Tab* tab, |
| absl::optional<int> from_model_index, |
| int to_model_index) const; |
| |
| // Closes the tab at |model_index|. |
| void CloseTabInternal(int model_index, CloseTabSource source); |
| |
| // Removes the tab at |index| from |tabs_|. |
| void RemoveTabFromViewModel(int index); |
| |
| // Cleans up the Tab from the TabStrip. This is called from the tab animation |
| // code and is not a general-purpose method. |
| void OnTabCloseAnimationCompleted(Tab* tab); |
| |
| // Invoked from StoppedDraggingTabs to cleanup |view|. If |view| is known |
| // |is_first_view| is set to true. |
| void StoppedDraggingView(TabSlotView* view, bool* is_first_view); |
| |
| // Invoked when a mouse event occurs over |source|. Potentially switches the |
| // |stacked_layout_|. |
| void UpdateStackedLayoutFromMouseEvent(views::View* source, |
| const ui::MouseEvent& event); |
| |
| // Computes and stores values derived from contrast ratios. |
| void UpdateContrastRatioValues(); |
| |
| // Determines whether a tab can be shifted by one in the direction of |offset| |
| // and moves it if possible. |
| void ShiftTabRelative(Tab* tab, int offset); |
| |
| // Determines whether a group can be shifted by one in the direction of |
| // |offset| and moves it if possible. |
| void ShiftGroupRelative(const tab_groups::TabGroupId& group, int offset); |
| |
| // -- Tab Resize Layout ----------------------------------------------------- |
| |
| // Perform an animated resize-relayout of the TabStrip immediately. |
| void ResizeLayoutTabs(); |
| |
| // Invokes ResizeLayoutTabs() as long as we're not in a drag session. If we |
| // are in a drag session this restarts the timer. |
| void ResizeLayoutTabsFromTouch(); |
| |
| // Restarts |resize_layout_timer_|. |
| void StartResizeLayoutTabsFromTouchTimer(); |
| |
| // Ensure that the message loop observer used for event spying is added and |
| // removed appropriately so we can tell when to resize layout the tab strip. |
| void AddMessageLoopObserver(); |
| void RemoveMessageLoopObserver(); |
| |
| // -- Link Drag & Drop ------------------------------------------------------ |
| |
| // Returns the bounds to render the drop at, in screen coordinates. Sets |
| // |is_beneath| to indicate whether the arrow is beneath the tab, or above |
| // it. |
| gfx::Rect GetDropBounds(int drop_index, |
| bool drop_before, |
| bool drop_in_group, |
| bool* is_beneath); |
| |
| // Show drop arrow with passed |tab_data_index| and |drop_before|. |
| // If |tab_data_index| is negative, the arrow will disappear. |
| void SetDropArrow(const absl::optional<BrowserRootView::DropIndex>& index); |
| |
| // Returns the image to use for indicating a drop on a tab. If is_down is |
| // true, this returns an arrow pointing down. |
| static gfx::ImageSkia* GetDropArrowImage(bool is_down); |
| |
| // -- Animations ------------------------------------------------------------ |
| |
| // Invoked prior to starting a new animation. |
| void PrepareForAnimation(); |
| |
| // Generates and sets the ideal bounds for each of the tabs as well as the new |
| // tab button. Note: Does not animate the tabs to those bounds so callers can |
| // use this information for other purposes - see AnimateToIdealBounds. |
| void UpdateIdealBounds(); |
| |
| // Generates and sets the ideal bounds for the pinned tabs. Returns the index |
| // to position the first non-pinned tab and sets |first_non_pinned_index| to |
| // the index of the first non-pinned tab. |
| int UpdateIdealBoundsForPinnedTabs(int* first_non_pinned_index); |
| |
| // Calculates the width that can be occupied by the tabs in the strip. This |
| // can differ from GetAvailableWidthForTabStrip() when in tab closing mode. |
| int CalculateAvailableWidthForTabs() const; |
| |
| // Returns the total width available for the TabStrip's use. |
| int GetAvailableWidthForTabStrip() const; |
| |
| // Starts various types of TabStrip animations. |
| void StartResizeLayoutAnimation(); |
| void StartPinnedTabAnimation(); |
| |
| // Returns true if the specified point in TabStrip coords is within the |
| // hit-test region of the specified Tab. |
| bool IsPointInTab(Tab* tab, const gfx::Point& point_in_tabstrip_coords); |
| |
| // -- Touch Layout ---------------------------------------------------------- |
| |
| // Returns the tab to use for event handling. This uses FindTabForEventFrom() |
| // to do the actual searching. This method should be called when |
| // |touch_layout_| is set. |
| Tab* FindTabForEvent(const gfx::Point& point); |
| |
| // Helper for FindTabForEvent(). Returns the tab to use for event handling |
| // starting at index |start| and iterating by |delta|. |
| Tab* FindTabForEventFrom(const gfx::Point& point, int start, int delta); |
| |
| // For a given point, finds a tab that is hit by the point. If the point hits |
| // an area on which two tabs are overlapping, the tab is selected as follows: |
| // - If one of the tabs is active, select it. |
| // - Select the left one. |
| // If no tabs are hit, returns null. This method should be called when |
| // |touch_layout_| is not set. |
| Tab* FindTabHitByPoint(const gfx::Point& point); |
| |
| // Creates/Destroys |touch_layout_| as necessary. |
| void SwapLayoutIfNecessary(); |
| |
| // Returns true if |touch_layout_| is needed. |
| bool NeedsTouchLayout() const; |
| |
| // Sets the value of |reset_to_shrink_on_exit_|. If true |mouse_watcher_| is |
| // used to track when the mouse truly exits the tabstrip and the stacked |
| // layout is reset. |
| void SetResetToShrinkOnExit(bool value); |
| |
| // Called whenever a tab animation has progressed. |
| void OnTabSlotAnimationProgressed(TabSlotView* view); |
| |
| // Called to update the visuals for a tab group when tabs in the group are |
| // moved or resized. |
| void UpdateTabGroupVisuals(tab_groups::TabGroupId tab_group_id); |
| |
| // views::View: |
| bool OnMousePressed(const ui::MouseEvent& event) override; |
| bool OnMouseDragged(const ui::MouseEvent& event) override; |
| void OnMouseReleased(const ui::MouseEvent& event) override; |
| void OnMouseCaptureLost() override; |
| void OnMouseMoved(const ui::MouseEvent& event) override; |
| void OnMouseEntered(const ui::MouseEvent& event) override; |
| void OnMouseExited(const ui::MouseEvent& event) override; |
| void AddedToWidget() override; |
| void RemovedFromWidget() override; |
| |
| // ui::EventHandler: |
| void OnGestureEvent(ui::GestureEvent* event) override; |
| |
| // views::ViewTargeterDelegate: |
| views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; |
| |
| // views::ViewObserver: |
| void OnViewFocused(views::View* observed_view) override; |
| void OnViewBlurred(views::View* observed_view) override; |
| |
| // views::WidgetObserver: |
| void OnWidgetActivationChanged(views::Widget* widget, bool active) override; |
| |
| // views::BoundsAnimatorObserver: |
| void OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) override {} |
| void OnBoundsAnimatorDone(views::BoundsAnimator* animator) override; |
| |
| void OnTouchUiChanged(); |
| |
| // Screen-reader-only announcements that depend on tab group titles. |
| void AnnounceTabAddedToGroup(tab_groups::TabGroupId group_id); |
| void AnnounceTabRemovedFromGroup(tab_groups::TabGroupId group_id); |
| |
| // -- Member Variables ------------------------------------------------------ |
| |
| base::ObserverList<TabStripObserver>::Unchecked observers_; |
| |
| // There is a one-to-one mapping between each of the tabs in the |
| // TabStripController (TabStripModel) and |tabs_|. Because we animate tab |
| // removal there exists a period of time where a tab is displayed but not in |
| // the model. When this occurs the tab is removed from |tabs_|, but remains |
| // in |layout_helper_| until the remove animation completes. |
| views::ViewModelT<Tab> tabs_; |
| |
| std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>> group_views_; |
| |
| std::unique_ptr<TabStripController> controller_; |
| |
| base::RepeatingCallback<int()> available_width_callback_; |
| |
| std::unique_ptr<TabStripLayoutHelper> layout_helper_; |
| |
| // Responsible for animating tabs in response to model changes. |
| views::BoundsAnimator bounds_animator_{this}; |
| |
| // Responsible for animating the scroll of the tab strip. |
| std::unique_ptr<gfx::LinearAnimation> tab_scrolling_animation_; |
| |
| // If this value is defined, it is used as the width to lay out tabs |
| // (instead of GetAvailableWidthForTabStrip()). It is defined when closing |
| // tabs with the mouse, and is used to control which tab will end up under the |
| // cursor after the close animation completes. |
| absl::optional<int> override_available_width_for_tabs_; |
| |
| // The background offset used by inactive tabs to match the frame image. |
| int background_offset_ = 0; |
| |
| // True if PrepareForCloseAt has been invoked. When true remove animations |
| // preserve current tab bounds. |
| bool in_tab_close_ = false; |
| |
| // Valid for the lifetime of a drag over us. |
| std::unique_ptr<DropArrow> drop_arrow_; |
| |
| // MouseWatcher is used for two things: |
| // . When a tab is closed to reset the layout. |
| // . When a mouse is used and the layout dynamically adjusts and is currently |
| // stacked (|stacked_layout_| is true). |
| std::unique_ptr<views::MouseWatcher> mouse_watcher_; |
| |
| // Size we last layed out at. |
| gfx::Size last_layout_size_; |
| |
| // The width available for tabs at the time of last layout. |
| int last_available_width_ = 0; |
| |
| // See description above stacked_layout(). |
| bool stacked_layout_ = false; |
| |
| // Should the layout dynamically adjust? |
| bool adjust_layout_ = false; |
| |
| // Only used while in touch mode. |
| std::unique_ptr<StackedTabStripLayout> touch_layout_; |
| |
| // If true the |stacked_layout_| is set to false when the mouse exits the |
| // tabstrip (as determined using MouseWatcher). |
| bool reset_to_shrink_on_exit_ = false; |
| |
| // Location of the mouse at the time of the last move. |
| gfx::Point last_mouse_move_location_; |
| |
| // Time of the last mouse move event. |
| base::TimeTicks last_mouse_move_time_; |
| |
| // Used to track the time needed to create a new tab from the new tab button. |
| absl::optional<base::TimeTicks> new_tab_button_pressed_start_time_; |
| |
| // Used for seek time metrics from the time the mouse enters the tabstrip. |
| absl::optional<base::TimeTicks> mouse_entered_tabstrip_time_; |
| |
| // Number of mouse moves. |
| int mouse_move_count_ = 0; |
| |
| // Timer used when a tab is closed and we need to relayout. Only used when a |
| // tab close comes from a touch device. |
| base::OneShotTimer resize_layout_timer_; |
| |
| // This represents the Tabs in |tabs_| that have been selected. |
| // |
| // Each time tab selection should change, this class will receive a |
| // SetSelection() callback with the new tab selection. That callback only |
| // includes the new selection model. This keeps track of the previous |
| // selection model, and is always consistent with |tabs_|. This must be |
| // updated to account for tab insertions/removals/moves. |
| ui::ListSelectionModel selected_tabs_; |
| |
| // When tabs are hovered, a radial highlight is shown and the tab opacity is |
| // adjusted using some value between |hover_opacity_min_| and |
| // |hover_opacity_max_| (depending on tab width). All these opacities depend |
| // on contrast ratios and are updated when colors or active state changes, |
| // so for efficiency's sake they are computed and stored once here instead |
| // of with each tab. Note: these defaults will be overwritten at construction |
| // except in cases where a unit test provides no controller_. |
| float hover_opacity_min_ = 1.0f; |
| float hover_opacity_max_ = 1.0f; |
| float radial_highlight_opacity_ = 1.0f; |
| |
| SkColor separator_color_ = gfx::kPlaceholderColor; |
| |
| std::unique_ptr<TabHoverCardController> hover_card_controller_; |
| |
| const base::CallbackListSubscription subscription_ = |
| ui::TouchUiController::Get()->RegisterCallback( |
| base::BindRepeating(&TabStrip::OnTouchUiChanged, |
| base::Unretained(this))); |
| |
| std::unique_ptr<TabDragContextImpl> drag_context_; |
| |
| TabContextMenuController context_menu_controller_{this}; |
| }; |
| |
| #endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_STRIP_H_ |