blob: e5c7fa34452e1d934558499ad665a180af4d7cbc [file] [log] [blame]
// Copyright 2019 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_TAB_GROUP_HEADER_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_GROUP_HEADER_H_
#include <string_view>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/time/time.h"
#include "chrome/browser/ui/views/tabs/tab_slot_view.h"
#include "components/tab_groups/tab_group_id.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/mojom/menu_source_type.mojom-forward.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/view_targeter_delegate.h"
#include "ui/views/widget/widget_observer.h"
class TabSlotController;
class TabGroupStyle;
struct TabSizeInfo;
class TabStyle;
namespace views {
class ImageView;
class Label;
class View;
} // namespace views
// View for tab group headers in the tab strip, which are markers of group
// boundaries. There is one header for each group, which is included in the tab
// strip flow and positioned left of the leftmost tab in the group.
class TabGroupHeader : public TabSlotView,
public views::ContextMenuController,
public views::ViewTargeterDelegate {
METADATA_HEADER(TabGroupHeader, TabSlotView)
public:
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kAttentionIndicatorViewElementId);
TabGroupHeader(TabSlotController& tab_slot_controller,
const tab_groups::TabGroupId& group,
const TabGroupStyle& style);
TabGroupHeader(const TabGroupHeader&) = delete;
TabGroupHeader& operator=(const TabGroupHeader&) = delete;
~TabGroupHeader() override;
void Init(const tab_groups::TabGroupId& group);
// TabSlotView:
bool OnKeyPressed(const ui::KeyEvent& event) override;
bool OnMousePressed(const ui::MouseEvent& event) override;
bool OnMouseDragged(const ui::MouseEvent& event) override;
void OnMouseReleased(const ui::MouseEvent& event) override;
void OnMouseEntered(const ui::MouseEvent& event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
void OnFocus() override;
void OnThemeChanged() override;
TabSlotView::ViewType GetTabSlotViewType() const override;
TabSizeInfo GetTabSizeInfo() const override;
gfx::Rect GetAnchorBoundsInScreen() const override;
void OnGroupContentsChanged();
// views::ContextMenuController:
void ShowContextMenuForViewImpl(
views::View* source,
const gfx::Point& point,
ui::mojom::MenuSourceType source_type) override;
// views::ViewTargeterDelegate:
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override;
// Updates our visual state according to the tab_groups::TabGroupVisualData
// for our group.
// TODO(crbug.com/372296676): Make TabGroupHeader observe the group for
// changes to cut down on the number of times we recalculate the view.
void VisualsChanged();
int GetCollapsedHeaderWidth() const;
// Removes {editor_bubble_tracker_} from observing the widget.
void RemoveObserverFromWidget(views::Widget* widget);
// Enables or disables attention indicator on a tab group.
void SetTabGroupNeedsAttention(bool needs_attention);
// Returns whether the attention indicator should be shown.
bool GetShowingAttentionIndicator();
// Returns the title text for testing.
std::u16string_view GetTitleTextForTesting() const;
bool is_collapsed_for_testing() const { return is_collapsed_; }
private:
friend class TabGroupEditorBubbleViewDialogBrowserTest;
FRIEND_TEST_ALL_PREFIXES(TabStripSaveBrowsertest, AttentionIndicatorIsShown);
// Calculate the width for this View.
int GetDesiredWidth() const;
// Determines if the sync icon should be shown in the header.
bool ShouldShowHeaderIcon() const;
// Updates the local is_collapsed_ state.
void SetCollapsedState();
void UpdateTitleView();
void UpdateSyncIconView();
void UpdateAttentionIndicatorView();
void UpdateIsCollapsed();
// Creates a squircle (cross between a square and a circle).
void CreateHeaderWithoutTitle();
// Creates a round rect, similar to the shape of a tab when hovered but not
// selected.
void CreateHeaderWithTitle();
void UpdateTooltipText();
void UpdateAccessibleName();
const raw_ref<TabSlotController> tab_slot_controller_;
// The title chip for the tab group header which comprises of title text if
// there is any, and a background color. The size and color of the chip are
// set in VisualsChanged().
const raw_ptr<views::View> title_chip_;
// The title of the tab group. Text and color of the title are set in
// VisualsChanged().
const raw_ptr<views::Label> title_;
// The sync icon that is displayed in the tab group header of saved groups in
// the tabstrip.
const raw_ptr<views::ImageView> sync_icon_;
// The circle indicator rendered after the title when a tab group has
// needs_attention_ set to true.
const raw_ptr<views::ImageView> attention_indicator_;
const raw_ref<const TabGroupStyle> group_style_;
const raw_ptr<const TabStyle> tab_style_;
// The current title of the group.
std::u16string group_title_;
// The current color of the group.
SkColor color_;
// Determines if we should show the header icon in front of the title.
bool should_show_header_icon_;
// Local saved collapsed state. When this differs from
// `TabSlotController::IsGroupCollapsed()`, then the collapsed state has
// changed in the model and we need to react to that.
bool is_collapsed_;
// Determines if the tab group should show the attention indicator.
bool needs_attention_ = false;
base::CallbackListSubscription title_text_changed_subscription_;
// Tracks whether our editor bubble is open. At most one can be open
// at once.
class EditorBubbleTracker : public views::WidgetObserver {
public:
explicit EditorBubbleTracker(TabSlotController& tab_slot_controller);
~EditorBubbleTracker() override;
void Opened(views::Widget* bubble_widget);
bool is_open() const { return is_open_; }
views::Widget* widget() const { return widget_; }
// views::WidgetObserver:
void OnWidgetDestroying(views::Widget* widget) override;
private:
bool is_open_ = false;
raw_ptr<views::Widget, AcrossTasksDanglingUntriaged> widget_;
// Outlives this because it's a dependency inversion interface for the
// header's parent View.
raw_ref<TabSlotController> tab_slot_controller_;
};
EditorBubbleTracker editor_bubble_tracker_;
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_GROUP_HEADER_H_