blob: 83c5f5fc985dbcda0d4b6a0444c10ff0e138928b [file] [log] [blame]
// Copyright (c) 2012 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_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_H_
#include <list>
#include <memory>
#include <string>
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "cc/paint/paint_record.h"
#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
#include "ui/base/layout.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/paint_throbber.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/glow_hover_controller.h"
#include "ui/views/masked_targeter_delegate.h"
#include "ui/views/view.h"
class AlertIndicatorButton;
class TabCloseButton;
class TabController;
class TabIcon;
namespace gfx {
class Animation;
class AnimationContainer;
class LinearAnimation;
class ThrobAnimation;
}
namespace views {
class Label;
}
///////////////////////////////////////////////////////////////////////////////
//
// A View that renders a Tab in a TabStrip.
//
///////////////////////////////////////////////////////////////////////////////
class Tab : public gfx::AnimationDelegate,
public views::ButtonListener,
public views::ContextMenuController,
public views::MaskedTargeterDelegate,
public views::View {
public:
// The Tab's class name.
static const char kViewClassName[];
// The combined width of the curves at the top and bottom of the endcap.
static constexpr float kMinimumEndcapWidth = 4;
Tab(TabController* controller, gfx::AnimationContainer* container);
~Tab() override;
TabController* controller() const { return controller_; }
// Used to set/check whether this Tab is being animated closed.
void set_closing(bool closing) { closing_ = closing; }
bool closing() const { return closing_; }
// See description above field.
void set_dragging(bool dragging) { dragging_ = dragging; }
bool dragging() const { return dragging_; }
// Used to mark the tab as having been detached. Once this has happened, the
// tab should be invisibly closed. This is irreversible.
void set_detached() { detached_ = true; }
bool detached() const { return detached_; }
// Returns the radius of the outer corners of the tab shape.
int GetCornerRadius() const;
// Returns the color used for the alert indicator icon.
SkColor GetAlertIndicatorColor(TabAlertState state) const;
// Returns the color to be used for the tab close button.
SkColor GetCloseTabButtonColor(views::Button::ButtonState button_state) const;
// Returns true if this tab is the active tab.
bool IsActive() const;
// Notifies the AlertIndicatorButton that the active state of this tab has
// changed.
void ActiveStateChanged();
// Called when the alert indicator has changed states.
void AlertStateChanged();
// Returns true if the tab is selected.
bool IsSelected() const;
// Sets the data this tabs displays. Should only be called after Tab is added
// to widget hierarchy.
void SetData(TabRendererData data);
const TabRendererData& data() const { return data_; }
// Redraws the loading animation if one is visible. Otherwise, no-op.
void StepLoadingAnimation();
// Starts/Stops a pulse animation.
void StartPulse();
void StopPulse();
// Sets the visibility of the indicator shown when the tab needs to indicate
// to the user that it needs their attention.
void SetTabNeedsAttention(bool attention);
// Set the background offset used to match the image in the inactive tab
// to the frame image.
void set_background_offset(const gfx::Point& offset) {
background_offset_ = offset;
}
// Returns true if this tab became the active tab selected in
// response to the last ui::ET_TAP_DOWN gesture dispatched to
// this tab. Only used for collecting UMA metrics.
// See ash/touch/touch_uma.cc.
bool tab_activated_with_last_tap_down() const {
return tab_activated_with_last_tap_down_;
}
views::GlowHoverController* hover_controller() {
return &hover_controller_;
}
// Returns the width of the largest part of the tab that is available for the
// user to click to select/activate the tab.
int GetWidthOfLargestSelectableRegion() const;
// Called when stacked layout changes and the close button may need to
// be updated.
void HideCloseButtonForInactiveTabsChanged() { Layout(); }
// Returns the minimum possible size of a single unselected Tab.
static gfx::Size GetMinimumInactiveSize();
// Returns the minimum possible size of a selected Tab. Selected tabs must
// always show a close button and have a larger minimum size than unselected
// tabs.
static gfx::Size GetMinimumActiveSize();
// Returns the preferred size of a single Tab, assuming space is
// available.
static gfx::Size GetStandardSize();
// Returns the width for pinned tabs. Pinned tabs always have this width.
static int GetPinnedWidth();
// Returns the inverse of the slope of the diagonal portion of the tab outer
// border. (This is a positive value, so it's specifically for the slope of
// the leading edge.)
//
// This returns the inverse (dx/dy instead of dy/dx) because we use exact
// values for the vertical distances between points and then compute the
// horizontal deltas from those.
static float GetInverseDiagonalSlope();
// Returns the overlap between adjacent tabs.
static int GetOverlap();
private:
friend class AlertIndicatorButtonTest;
friend class TabTest;
friend class TabStripTest;
FRIEND_TEST_ALL_PREFIXES(TabStripTest, TabCloseButtonVisibilityWhenStacked);
FRIEND_TEST_ALL_PREFIXES(TabStripTest,
TabCloseButtonVisibilityWhenNotStacked);
// gfx::AnimationDelegate:
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::ContextMenuController:
void ShowContextMenuForView(views::View* source,
const gfx::Point& point,
ui::MenuSourceType source_type) override;
// views::MaskedTargeterDelegate:
bool GetHitTestMask(gfx::Path* mask) const override;
// views::View:
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
void OnPaint(gfx::Canvas* canvas) override;
void PaintChildren(const views::PaintInfo& info) override;
void Layout() override;
void OnThemeChanged() override;
const char* GetClassName() const override;
bool GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const override;
bool GetTooltipTextOrigin(const gfx::Point& p,
gfx::Point* origin) const override;
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 OnMouseEntered(const ui::MouseEvent& event) override;
void OnMouseMoved(const ui::MouseEvent& event) override;
void OnMouseExited(const ui::MouseEvent& event) override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
// ui::EventHandler:
void OnGestureEvent(ui::GestureEvent* event) override;
// Forces the tab to the right of this tab to repaint.
void RepaintSubsequentTab();
// Invoked from Layout to adjust the position of the favicon or alert
// indicator for pinned tabs. The visual_width parameter is how wide the
// icon looks (rather than how wide the bounds are).
void MaybeAdjustLeftForPinnedTab(gfx::Rect* bounds, int visual_width) const;
// Paints with the normal tab style. If |clip| is non-empty, the tab border
// should be clipped against it.
void PaintTab(gfx::Canvas* canvas, const gfx::Path& clip);
// Paints the background of an inactive tab.
void PaintInactiveTabBackground(gfx::Canvas* canvas, const gfx::Path& clip);
// Paints a tab background using the image defined by |fill_id| at the
// provided offset. If |fill_id| is 0, it will fall back to using the solid
// color defined by the theme provider and ignore the offset.
void PaintTabBackground(gfx::Canvas* canvas,
bool active,
int fill_id,
int y_offset,
const gfx::Path* clip);
// Helper methods for PaintTabBackground.
void PaintTabBackgroundFill(gfx::Canvas* canvas,
const gfx::Path& fill_path,
bool active,
bool hover,
SkColor active_color,
SkColor inactive_color,
int fill_id,
int y_offset);
void PaintTabBackgroundStroke(gfx::Canvas* canvas,
const gfx::Path& fill_path,
const gfx::Path& stroke_path,
bool active,
SkColor color);
// Paints the separator line on the left edge of the tab if in material
// refresh mode. The painted color is derived from the inactive tab color.
void PaintSeparator(gfx::Canvas* canvas, SkColor inactive_color);
// Computes which icons are visible in the tab. Should be called everytime
// before layout is performed.
void UpdateIconVisibility();
// Returns whether the tab should be rendered as a normal tab as opposed to a
// pinned tab.
bool ShouldRenderAsNormalTab() const;
// Gets the throb value for the tab. When a tab is not selected the
// active background is drawn at |GetThrobValue()|%. This is used for hover,
// mini tab title change and pulsing.
double GetThrobValue();
// Recalculates the correct |button_color_| and resets the title, alert
// indicator, and close button colors if necessary. This should be called any
// time the theme or active state may have changed.
void OnButtonColorMaybeChanged();
// The controller, never NULL.
TabController* const controller_;
TabRendererData data_;
// True if the tab is being animated closed.
bool closing_ = false;
// True if the tab is being dragged.
bool dragging_ = false;
// True if the tab has been detached.
bool detached_ = false;
// Whole-tab throbbing "pulse" animation.
gfx::ThrobAnimation pulse_animation_;
scoped_refptr<gfx::AnimationContainer> animation_container_;
TabIcon* icon_ = nullptr;
AlertIndicatorButton* alert_indicator_button_ = nullptr;
TabCloseButton* close_button_ = nullptr;
views::Label* title_;
// The title's bounds are animated when switching between showing and hiding
// the tab's favicon/throbber.
gfx::Rect start_title_bounds_;
gfx::Rect target_title_bounds_;
gfx::LinearAnimation title_animation_;
bool tab_activated_with_last_tap_down_ = false;
views::GlowHoverController hover_controller_;
// The offset used to paint the inactive background image.
gfx::Point background_offset_;
// For narrow tabs, we show the favicon even if it won't completely fit.
// In this case, we need to center the favicon within the tab; it will be
// clipped to fit.
bool center_favicon_ = false;
// Whether we're showing the icon. It is cached so that we can detect when it
// changes and layout appropriately.
bool showing_icon_ = false;
// Whether we're showing the alert indicator. It is cached so that we can
// detect when it changes and layout appropriately.
bool showing_alert_indicator_ = false;
// Whether we are showing the close button. It is cached so that we can
// detect when it changes and layout appropriately.
bool showing_close_button_ = false;
// When the close button will be visible on inactive tabs, we add additional
// padding to the left of the favicon to balance the whitespace inside the
// non-hovered close button image; otherwise, the tab contents look too close
// to the left edge. If the tab close button isn't visible on inactive tabs,
// we let the tab contents take the full width of the tab, to maximize visible
// content on tiny tabs. We base the determination on the inactive tab close
// button state so that when a tab is activated its contents don't suddenly
// shift.
bool extra_padding_before_content_ = false;
// The current color of the alert indicator and close button icons.
SkColor button_color_ = SK_ColorTRANSPARENT;
class BackgroundCache {
public:
BackgroundCache();
~BackgroundCache();
bool CacheKeyMatches(float scale,
const gfx::Size& size,
SkColor active_color,
SkColor inactive_color,
SkColor stroke_color) {
return scale_ == scale && size_ == size &&
active_color_ == active_color &&
inactive_color_ == inactive_color && stroke_color_ == stroke_color;
}
void SetCacheKey(float scale,
const gfx::Size& size,
SkColor active_color,
SkColor inactive_color,
SkColor stroke_color) {
scale_ = scale;
size_ = size;
active_color_ = active_color;
inactive_color_ = inactive_color;
stroke_color_ = stroke_color;
}
// The PaintRecords being cached based on the input parameters.
sk_sp<cc::PaintRecord> fill_record;
sk_sp<cc::PaintRecord> stroke_record;
private:
// Parameters used to construct the PaintRecords.
float scale_ = 0.f;
gfx::Size size_;
SkColor active_color_ = 0;
SkColor inactive_color_ = 0;
SkColor stroke_color_ = 0;
};
// Cache of the paint output for tab backgrounds.
BackgroundCache background_active_cache_;
BackgroundCache background_inactive_cache_;
DISALLOW_COPY_AND_ASSIGN(Tab);
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_H_