1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_
#define UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/metadata/view_factory.h"
namespace views {
class Label;
class TabbedPaneTab;
class TabbedPaneListener;
class TabbedPaneTabStrip;
namespace test {
class TabbedPaneAccessibilityMacTest;
class TabbedPaneWithWidgetTest;
} // namespace test
// TabbedPane is a view that shows tabs. When the user clicks on a tab, the
// associated view is displayed.
// Support for horizontal-highlight and vertical-border modes is limited and
// may require additional polish.
class VIEWS_EXPORT TabbedPane : public FlexLayoutView {
METADATA_HEADER(TabbedPane, FlexLayoutView)
public:
// The orientation of the tab alignment.
enum class Orientation {
kHorizontal,
kVertical,
};
// The style of the tab strip.
enum class TabStripStyle {
kBorder, // Draw border around the selected tab.
kHighlight, // Highlight background and text of the selected tab.
kCompactWithIcon, // Draw an icon, shrink the highlight bar to icon+text
kWithIcon, // Draw an icon, expand the highlight bar to entire tab.
};
explicit TabbedPane(Orientation orientation = Orientation::kHorizontal,
TabStripStyle style = TabStripStyle::kBorder,
bool scrollable = false);
TabbedPane(const TabbedPane&) = delete;
TabbedPane& operator=(const TabbedPane&) = delete;
~TabbedPane() override;
TabbedPaneListener* GetListener() const;
void SetListener(TabbedPaneListener* listener);
// Returns the index of the currently selected tab, or
// TabStrip::kNoSelectedTab if no tab is selected.
size_t GetSelectedTabIndex() const;
// Returns the number of tabs.
size_t GetTabCount() const;
// Adds a new tab at the end of this TabbedPane with the specified |title|.
// |contents| is the view displayed when the tab is selected and is owned by
// the TabbedPane.
template <typename T>
T* AddTab(const std::u16string& title,
std::unique_ptr<T> contents,
const gfx::VectorIcon* tab_icon = nullptr) {
return AddTabAtIndex(GetTabCount(), title, std::move(contents), tab_icon);
}
// Adds a new tab at |index| with |title|. |contents| is the view displayed
// when the tab is selected and is owned by the TabbedPane. If the tabbed pane
// is currently empty, the new tab is selected.
template <typename T>
T* AddTabAtIndex(size_t index,
const std::u16string& title,
std::unique_ptr<T> contents,
const gfx::VectorIcon* tab_icon = nullptr) {
T* result = contents.get();
AddTabInternal(index, title, std::move(contents), tab_icon);
return result;
}
// Selects the tab at |index|, which must be valid.
void SelectTabAt(size_t index, bool animate = true);
// Selects |tab| (the tabstrip view, not its content) if it is valid.
void SetTabContentVisibility(size_t tab_index, bool visible);
// Calls the FocusManager (if it exists), and gives focus to the view at
// |tab_index|.
void MaybeSetFocusedView(size_t tab_index);
// Gets the scroll view containing the tab strip, if it exists
ScrollView* GetScrollView();
// Gets the orientation of the tab alignment.
Orientation GetOrientation() const;
// Gets the style of the tab strip.
TabStripStyle GetStyle() const;
// Returns the tab at the given index.
TabbedPaneTab* GetTabAt(size_t index);
// Returns the View associated with a specific tab.
const views::View* GetTabContents(size_t index) const;
views::View* GetTabContentsForTesting(size_t index);
// Updates the view's accessible name based on the currently selected tab's
// title.
void UpdateAccessibleName();
// Sets whether a divider will be drawn underneath the Tab Strip.
void SetDrawTabDivider(bool draw);
private:
friend class FocusTraversalTest;
friend class TabbedPaneTab;
friend class TabbedPaneTabStrip;
friend class test::TabbedPaneWithWidgetTest;
friend class test::TabbedPaneAccessibilityMacTest;
// Adds a new tab at |index| with |title|. |contents| is the view displayed
// when the tab is selected and is owned by the TabbedPane. If the tabbed pane
// is currently empty, the new tab is selected.
void AddTabInternal(size_t index,
const std::u16string& title,
std::unique_ptr<View> contents,
const gfx::VectorIcon* tab_icon = nullptr);
// Get the TabbedPaneTab (the tabstrip view, not its content) at the selected
// index.
TabbedPaneTab* GetSelectedTab();
// View:
gfx::Size CalculatePreferredSize(
const SizeBounds& available_size) const override;
// The tab strip and contents container. The child indices of these members
// correspond to match each TabbedPaneTab with its respective content View.
raw_ptr<TabbedPaneTabStrip> tab_strip_ = nullptr;
raw_ptr<View> contents_ = nullptr;
// The scroll view containing the tab strip, if |scrollable| is specified on
// creation.
raw_ptr<ScrollView> scroll_view_ = nullptr;
};
// The tab view shown in the tab strip.
class VIEWS_EXPORT TabbedPaneTab : public View {
METADATA_HEADER(TabbedPaneTab, View)
public:
static constexpr int kDefaultIconSize = 16;
static constexpr int kDefaultTitleLeftMargin = kDefaultIconSize / 2;
static constexpr int kDefaultHorizontalTabHeight = 32;
static constexpr int kMinimumVerticalTabWidth = 192;
TabbedPaneTab(TabbedPaneTabStrip* tab_strip,
const std::u16string& title,
const gfx::VectorIcon* tab_icon);
TabbedPaneTab(const TabbedPaneTab&) = delete;
TabbedPaneTab& operator=(const TabbedPaneTab&) = delete;
~TabbedPaneTab() override;
bool selected() const { return selected_; }
void SetSelected(bool selected);
std::u16string_view GetTitleText() const;
void SetTitleText(std::u16string_view text);
void SetTitleMargin(const gfx::Insets& margin);
void SetIconMargin(const gfx::Insets& margin);
void SetTabOutsets(const gfx::Outsets& outsets);
void SetHeight(int height);
// Overridden from View:
bool OnMousePressed(const ui::MouseEvent& event) override;
void OnMouseEntered(const ui::MouseEvent& event) override;
void OnMouseExited(const ui::MouseEvent& event) override;
void OnGestureEvent(ui::GestureEvent* event) override;
gfx::Size CalculatePreferredSize(
const SizeBounds& available_size) const override;
bool HandleAccessibleAction(const ui::AXActionData& action_data) override;
void OnFocus() override;
void OnBlur() override;
bool OnKeyPressed(const ui::KeyEvent& event) override;
void OnThemeChanged() override;
void UpdateEnabledColor(bool enabled);
private:
enum class State {
kInactive,
kActive,
kHovered,
};
void SetState(State state);
// Called whenever |state_| changes.
void OnStateChanged();
// views::View:
void OnPaint(gfx::Canvas* canvas) override;
void UpdatePreferredTitleWidth();
void UpdateTitleColor();
void UpdateIconColor();
void UpdateAccessibleName();
void UpdateAccessibleSelection();
ui::ImageModel GetImageModelForTab(ui::ColorId color_id) const;
ui::ColorId GetIconTitleColor() const;
raw_ptr<TabbedPaneTabStrip> tab_strip_;
raw_ptr<const gfx::VectorIcon> icon_for_tab_;
raw_ptr<ImageView> icon_view_ = nullptr;
raw_ptr<Label> title_ = nullptr;
gfx::Outsets tab_outsets_ = gfx::Outsets::VH(0, 0);
// The preferred title width is the maximum width between inactive and active
// states (font changes). See UpdatePreferredTitleWidth() for more details.
int preferred_title_width_;
int height_ = kDefaultHorizontalTabHeight;
State state_ = State::kActive;
bool selected_ = false;
base::CallbackListSubscription title_text_changed_callback_;
};
// The tab strip shown above/left of the tab contents.
class VIEWS_EXPORT TabbedPaneTabStrip : public View,
public gfx::AnimationDelegate {
METADATA_HEADER(TabbedPaneTabStrip, View)
public:
// The return value of GetSelectedTabIndex() when no tab is selected.
static constexpr size_t kNoSelectedTab = static_cast<size_t>(-1);
TabbedPaneTabStrip(TabbedPane::Orientation orientation,
TabbedPane::TabStripStyle style,
raw_ptr<TabbedPane> tabbed_pane);
TabbedPaneTabStrip(const TabbedPaneTabStrip&) = delete;
TabbedPaneTabStrip& operator=(const TabbedPaneTabStrip&) = delete;
~TabbedPaneTabStrip() override;
TabbedPaneListener* listener() { return listener_; }
void set_listener(TabbedPaneListener* listener) { listener_ = listener; }
// Adds a new TabbedPaneTab as a child of this View. This method should only
// be used when TabbedPaneTabStrip is instantiated as a standalone component.
TabbedPaneTab* AddTab(const std::u16string& title,
const gfx::VectorIcon* tab_icon);
TabbedPaneTab* AddTabAt(const std::u16string& title,
const gfx::VectorIcon* tab_icon,
size_t index);
// AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
// Called by TabbedPaneTabStrip when the selected tab changes. This function
// is only called if |from_tab| is not null, i.e., there was a previously
// selected tab.
void OnSelectedTabChanged(TabbedPaneTab* from_tab,
TabbedPaneTab* to_tab,
bool animate = true);
// Attempts to select the provided tab. Returns true if the new tab was
// selected, or false if no work was done (i.e., |new_selected_tab| is the
// currently selected tab).
bool SelectTab(TabbedPaneTab* new_selected_tab, bool animate = true);
// Updates the visibility of the content associated with the tab at
// |tab_index| if there is a |tabbed_pane_| parented to this view.
void MaybeUpdateTabContentVisibility(size_t tab_index, bool visible);
// Dispatches an accessibility event to the parent |tabbed_pane_| if it
// exists, otherwise the event is dispatched on behalf of this View.
void NotifyNewAccessibilityEvent(ax::mojom::Event event_type,
bool send_native_event);
// Moves the selection by |delta| tabs, where negative delta means leftwards
// and positive delta means rightwards. Returns whether the move was
// performed. This only fails if the delta results in currently selected tab.
bool MoveSelectionBy(int delta);
TabbedPaneTab* GetSelectedTab() const;
TabbedPaneTab* GetTabAtDeltaFromSelected(int delta) const;
TabbedPaneTab* GetTabAtIndex(size_t index) const;
size_t GetIndexForTab(TabbedPaneTab* index) const;
size_t GetSelectedTabIndex() const;
size_t GetTabCount() const;
// Sets the default flex of the tab strip. Useful for adding custom padding
// instead of expecting the tab strip to stretch across its parent container.
void SetDefaultFlex(int flex);
// Sets how far apart the tabs will be positioned.
void SetTabSpacing(int spacing);
TabbedPane::Orientation GetOrientation() const;
TabbedPane::TabStripStyle GetStyle() const;
// Returns whether an Icon should be rendered for the TabbedPaneTab children.
bool HasIconStyle() const;
// Updates its own accessible name, and also calls TabbedPane's
// UpdateAccessibleName method if |tabbed_pane_| is defined.
void UpdateAccessibleName();
// Sets whether a divider will be drawn underneath the Tab Strip.
void SetDrawTabDivider(bool draw);
protected:
// View:
void OnPaintBorder(gfx::Canvas* canvas) override;
private:
struct Coordinates {
int start, end;
};
// Returns the beginning and ending distances for the icon+label in a tab.
// start is the distance from the origin to the left-side of the icon,
// and end is the distance from the origin to the right-side of the text.
// (x) Label
// -------start-------^ ^
// -------end-----------------^
Coordinates GetIconLabelStartEndingX(TabbedPaneTab* tab);
// view::View
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
// The orientation of the tab alignment.
const TabbedPane::Orientation orientation_;
// The style of the tab strip.
const TabbedPane::TabStripStyle style_;
// Animations for expanding and contracting the selection bar. When changing
// selections, the selection bar first grows to encompass both the old and new
// selections, then shrinks to encompass only the new selection. The rates of
// expansion and contraction each follow the cubic bezier curves used in
// gfx::Tween; see TabStrip::OnPaintBorder for details.
std::unique_ptr<gfx::LinearAnimation> expand_animation_ =
std::make_unique<gfx::LinearAnimation>(this);
std::unique_ptr<gfx::LinearAnimation> contract_animation_ =
std::make_unique<gfx::LinearAnimation>(this);
// The x-coordinate ranges of the old selection and the new selection.
Coordinates animating_from_;
Coordinates animating_to_;
// A listener notified when tab selection changes. Weak, not owned.
raw_ptr<TabbedPaneListener> listener_ = nullptr;
// An optional parent container which connects Tabs in the TabStrip to
// content views.
raw_ptr<TabbedPane> tabbed_pane_;
// Whether to draw the unselected divider below the tabs. Useful for when
// the caller wants to use a custom divider instead.
bool draw_tab_divider_ = true;
};
BEGIN_VIEW_BUILDER(VIEWS_EXPORT, TabbedPane, FlexLayoutView)
VIEW_BUILDER_METHOD_ALIAS(AddTab,
AddTab<View>,
const std::u16string&,
std::unique_ptr<View>,
const gfx::VectorIcon*)
END_VIEW_BUILDER
} // namespace views
DEFINE_VIEW_BUILDER(VIEWS_EXPORT, TabbedPane)
#endif // UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_
|