blob: 86f6ffb08697485dd508297b07e11286b6a7dd9a [file] [log] [blame]
// 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.
#include "chrome/browser/ui/views/tabs/new_tab_button.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h"
#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
#include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/grit/theme_resources.h"
#include "components/feature_engagement/buildflags.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkMaskFilter.h"
#include "third_party/skia/include/effects/SkLayerDrawLooper.h"
#include "third_party/skia/include/pathops/SkPathOps.h"
#include "ui/base/default_theme_provider.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/widget/widget.h"
#if defined(OS_WIN)
#include "ui/display/win/screen_win.h"
#include "ui/gfx/win/hwnd_util.h"
#include "ui/views/win/hwnd_util.h"
#endif
#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker.h"
#include "chrome/browser/feature_engagement/new_tab/new_tab_tracker_factory.h"
#include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h"
#endif
using MD = ui::MaterialDesignController;
namespace {
constexpr int kDistanceBetweenIcons = 6;
constexpr int kStrokeThickness = 1;
// Returns the size of the button without any spacing/padding.
gfx::Size GetButtonSize(bool is_incognito) {
const gfx::Size sizes[] = {
{36, 18}, {39, 21}, {(is_incognito ? 42 : 24), 24}, {28, 28}, {28, 28}};
return sizes[MD::GetMode()];
}
sk_sp<SkDrawLooper> CreateShadowDrawLooper(SkColor color) {
SkLayerDrawLooper::Builder looper_builder;
looper_builder.addLayer();
SkLayerDrawLooper::LayerInfo layer_info;
layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
layer_info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
layer_info.fColorMode = SkBlendMode::kDst;
layer_info.fOffset.set(0, 1);
SkPaint* layer_paint = looper_builder.addLayer(layer_info);
layer_paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.5));
layer_paint->setColorFilter(
SkColorFilter::MakeModeFilter(color, SkBlendMode::kSrcIn));
return looper_builder.detach();
}
// Returns the ID of the resource that should be used for the button fill if
// any. |has_custom_image| will be set to true if the images of either the
// tab, the frame background, (or the toolbar if |is_non_refresh_touch_ui| is
// true) have been customized.
int GetButtonFillResourceIdIfAny(const TabStrip* tab_strip,
const ui::ThemeProvider* theme_provider,
bool is_non_refresh_touch_ui,
bool* has_custom_image) {
if (!is_non_refresh_touch_ui)
return tab_strip->GetBackgroundResourceId(has_custom_image);
constexpr int kTouchBackgroundId = IDR_THEME_TOOLBAR;
*has_custom_image = theme_provider->HasCustomImage(kTouchBackgroundId);
return kTouchBackgroundId;
}
} // namespace
NewTabButton::NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener)
: views::ImageButton(listener),
tab_strip_(tab_strip),
is_incognito_(tab_strip->IsIncognito()) {
set_animate_on_state_change(true);
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
set_triggerable_event_flags(triggerable_event_flags() |
ui::EF_MIDDLE_MOUSE_BUTTON);
#endif
if (MD::IsNewerMaterialUi()) {
// Initialize the ink drop mode for a ripple highlight on button press.
ink_drop_container_ = new views::InkDropContainerView();
AddChildView(ink_drop_container_);
ink_drop_container_->SetVisible(false);
SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
set_ink_drop_visible_opacity(0.08f);
SetFocusPainter(nullptr);
focus_ring_ = views::FocusRing::Install(this);
}
// In newer material UI, the button is placed vertically exactly in the
// center of the tabstrip. In older UI, the new tab button is placed at a
// fixed distance from the bottom of the tabstrip.
const int extra_vertical_space = GetLayoutConstant(TAB_HEIGHT) -
GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) -
GetButtonSize(is_incognito_).height();
constexpr int kNewTabButtonBottomOffset = 4;
const int top = MD::IsNewerMaterialUi()
? (extra_vertical_space / 2)
: (extra_vertical_space - kNewTabButtonBottomOffset);
const int horizontal = MD::IsRefreshUi() ? 8 : 0;
SetBorder(
views::CreateEmptyBorder(gfx::Insets(top, horizontal, 0, horizontal)));
}
NewTabButton::~NewTabButton() {
if (destroyed_)
*destroyed_ = true;
}
// static
void NewTabButton::ShowPromoForLastActiveBrowser() {
BrowserView* browser = static_cast<BrowserView*>(
BrowserList::GetInstance()->GetLastActive()->window());
browser->tabstrip()->new_tab_button()->ShowPromo();
}
// static
void NewTabButton::CloseBubbleForLastActiveBrowser() {
BrowserView* browser = static_cast<BrowserView*>(
BrowserList::GetInstance()->GetLastActive()->window());
browser->tabstrip()->new_tab_button()->CloseBubble();
}
void NewTabButton::ShowPromo() {
DCHECK(!new_tab_promo_);
// Owned by its native widget. Will be destroyed as its widget is destroyed.
new_tab_promo_ = NewTabPromoBubbleView::CreateOwned(this);
new_tab_promo_observer_.Add(new_tab_promo_->GetWidget());
SchedulePaint();
}
void NewTabButton::CloseBubble() {
if (new_tab_promo_)
new_tab_promo_->CloseBubble();
}
void NewTabButton::FrameColorsChanged() {
if (MD::IsRefreshUi())
UpdateInkDropBaseColor();
}
void NewTabButton::AnimateInkDropToStateForTesting(views::InkDropState state) {
GetInkDrop()->AnimateToState(state);
}
#if defined(OS_WIN)
void NewTabButton::OnMouseReleased(const ui::MouseEvent& event) {
if (event.IsOnlyRightMouseButton()) {
gfx::Point point = event.location();
views::View::ConvertPointToScreen(this, &point);
point = display::win::ScreenWin::DIPToScreenPoint(point);
bool destroyed = false;
destroyed_ = &destroyed;
gfx::ShowSystemMenuAtPoint(views::HWNDForView(this), point);
if (destroyed)
return;
destroyed_ = NULL;
SetState(views::Button::STATE_NORMAL);
return;
}
views::ImageButton::OnMouseReleased(event);
}
#endif
void NewTabButton::OnGestureEvent(ui::GestureEvent* event) {
// Consume all gesture events here so that the parent (Tab) does not
// start consuming gestures.
views::ImageButton::OnGestureEvent(event);
event->SetHandled();
}
void NewTabButton::AddInkDropLayer(ui::Layer* ink_drop_layer) {
DCHECK(ink_drop_layer->bounds().size() == GetContentsBounds().size());
DCHECK(ink_drop_container_->bounds().size() == GetContentsBounds().size());
ink_drop_container_->AddInkDropLayer(ink_drop_layer);
InstallInkDropMask(ink_drop_layer);
}
void NewTabButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
ResetInkDropMask();
ink_drop_container_->RemoveInkDropLayer(ink_drop_layer);
}
std::unique_ptr<views::InkDropRipple> NewTabButton::CreateInkDropRipple()
const {
const gfx::Rect contents_bounds = GetContentsBounds();
return std::make_unique<views::FloodFillInkDropRipple>(
contents_bounds.size(), gfx::Insets(),
GetInkDropCenterBasedOnLastEvent() - contents_bounds.OffsetFromOrigin(),
GetInkDropBaseColor(), ink_drop_visible_opacity());
}
std::unique_ptr<views::InkDropHighlight> NewTabButton::CreateInkDropHighlight()
const {
const gfx::Rect bounds(GetContentsBounds().size());
auto highlight = CreateDefaultInkDropHighlight(
gfx::RectF(bounds).CenterPoint(), bounds.size());
highlight->set_visible_opacity(0.1f);
return highlight;
}
void NewTabButton::NotifyClick(const ui::Event& event) {
ImageButton::NotifyClick(event);
GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
}
std::unique_ptr<views::InkDrop> NewTabButton::CreateInkDrop() {
std::unique_ptr<views::InkDropImpl> ink_drop =
std::make_unique<views::InkDropImpl>(this, GetContentsBounds().size());
ink_drop->SetAutoHighlightMode(views::InkDropImpl::AutoHighlightMode::NONE);
ink_drop->SetShowHighlightOnHover(true);
UpdateInkDropBaseColor();
return ink_drop;
}
std::unique_ptr<views::InkDropMask> NewTabButton::CreateInkDropMask() const {
return std::make_unique<views::RoundRectInkDropMask>(
GetContentsBounds().size(), gfx::Insets(), GetCornerRadius());
}
void NewTabButton::PaintButtonContents(gfx::Canvas* canvas) {
gfx::ScopedCanvas scoped_canvas(canvas);
const gfx::Rect contents_bounds = GetContentsBounds();
canvas->Translate(contents_bounds.OffsetFromOrigin());
const float scale = canvas->image_scale();
const bool pressed = state() == views::Button::STATE_PRESSED;
const SkColor stroke_color =
new_tab_promo_observer_.IsObservingSources()
? color_utils::AlphaBlend(
SK_ColorBLACK,
GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_ProminentButtonColor),
0x70)
: tab_strip_->GetToolbarTopSeparatorColor();
// Fill.
SkPath fill, stroke;
const bool non_refresh_touch_ui =
MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
fill =
MD::IsNewerMaterialUi()
? GetNewerMaterialUiButtonPath(0, scale, false, non_refresh_touch_ui)
: GetMaterialUiButtonPath(0, contents_bounds.height(), scale, false,
true);
// The ink drop is used to represent the pressed state under Refresh.
PaintFill(!MD::IsRefreshUi() && pressed, scale, fill, canvas);
// Stroke.
if (!MD::IsRefreshUi())
GetBorderPath(0, scale, false, &stroke);
if (MD::IsNewerMaterialUi()) {
const int plus_icon_size = MD::IsTouchOptimizedUiEnabled() ? 14 : 12;
const int plus_icon_offset = GetCornerRadius() - (plus_icon_size / 2);
PaintPlusIcon(canvas, plus_icon_offset, plus_icon_size);
cc::PaintFlags paint_flags;
paint_flags.setAntiAlias(true);
if (ShouldDrawIncognitoIcon()) {
DCHECK(!incognito_icon_.isNull());
canvas->DrawImageInt(
incognito_icon_,
plus_icon_offset + plus_icon_size + kDistanceBetweenIcons,
plus_icon_offset, paint_flags);
}
if (!MD::IsRefreshUi()) {
// Draw stroke.
// In the touch-optimized UI design, the new tab button is rendered flat,
// regardless of whether pressed or not (i.e. we don't emulate a pushed
// button by drawing a drop shadow). Instead, we're using an ink drop
// ripple effect.
// Here we want to make sure the stroke width is 1px regardless of the
// device scale factor, so undo the scale.
canvas->UndoDeviceScaleFactor();
Op(stroke, fill, kDifference_SkPathOp, &stroke);
paint_flags.setColor(stroke_color);
canvas->DrawPath(stroke, paint_flags);
}
} else {
// We want to draw a drop shadow either inside or outside the stroke,
// depending on whether we're pressed; so, either clip out what's outside
// the stroke, or clip out the fill inside it.
canvas->UndoDeviceScaleFactor();
if (pressed)
canvas->ClipPath(stroke, true);
Op(stroke, fill, kDifference_SkPathOp, &stroke);
if (!pressed)
canvas->sk_canvas()->clipPath(fill, SkClipOp::kDifference, true);
// Now draw the stroke and shadow; the stroke will always be visible, while
// the shadow will be affected by the clip we set above.
cc::PaintFlags flags;
flags.setAntiAlias(true);
const float alpha = SkColorGetA(stroke_color);
const SkAlpha shadow_alpha =
base::saturated_cast<SkAlpha>(std::round(2.1875f * alpha));
flags.setLooper(
CreateShadowDrawLooper(SkColorSetA(stroke_color, shadow_alpha)));
const SkAlpha path_alpha = static_cast<SkAlpha>(
std::round((pressed ? 0.875f : 0.609375f) * alpha));
flags.setColor(SkColorSetA(stroke_color, path_alpha));
canvas->DrawPath(stroke, flags);
}
}
void NewTabButton::Layout() {
ImageButton::Layout();
if (MD::IsNewerMaterialUi()) {
if (ShouldDrawIncognitoIcon() && incognito_icon_.isNull())
InitIncognitoIcon();
// TODO(pkasting): Instead of setting this bounds rect, maybe have the
// container match the view bounds, then undo the coordinate transforms in
// the ink drop creation methods above.
const gfx::Rect contents_bounds = GetContentsBounds();
ink_drop_container_->SetBoundsRect(contents_bounds);
SkPath path;
path.addOval(gfx::RectToSkRect(contents_bounds));
focus_ring_->SetPath(path);
}
}
void NewTabButton::OnThemeChanged() {
ImageButton::OnThemeChanged();
if (!MD::IsNewerMaterialUi())
return;
if (ShouldDrawIncognitoIcon())
InitIncognitoIcon();
UpdateInkDropBaseColor();
}
gfx::Size NewTabButton::CalculatePreferredSize() const {
gfx::Size size = GetButtonSize(is_incognito_);
const auto insets = GetInsets();
size.Enlarge(insets.width(), insets.height());
return size;
}
void NewTabButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
const gfx::Size ink_drop_size = GetContentsBounds().size();
GetInkDrop()->HostSizeChanged(ink_drop_size);
UpdateInkDropMaskLayerSize(ink_drop_size);
}
bool NewTabButton::GetHitTestMask(gfx::Path* mask) const {
DCHECK(mask);
SkPath border;
const gfx::Point contents_origin = GetContentsBounds().origin();
const float scale = GetWidget()->GetCompositor()->device_scale_factor();
// TODO(pkasting): Fitts' Law horizontally when appropriate.
GetBorderPath(contents_origin.y() * scale, scale,
tab_strip_->SizeTabButtonToTopOfTabStrip(), &border);
border.offset(contents_origin.x(), 0);
mask->addPath(border, SkMatrix::MakeScale(1 / scale));
return true;
}
void NewTabButton::OnWidgetDestroying(views::Widget* widget) {
#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
feature_engagement::NewTabTrackerFactory::GetInstance()
->GetForProfile(tab_strip_->controller()->GetProfile())
->OnPromoClosed();
#endif
new_tab_promo_observer_.Remove(widget);
new_tab_promo_ = nullptr;
// When the promo widget is destroyed, the NewTabButton needs to be
// recolored.
SchedulePaint();
}
bool NewTabButton::ShouldDrawIncognitoIcon() const {
return is_incognito_ && MD::GetMode() == MD::MATERIAL_TOUCH_OPTIMIZED;
}
int NewTabButton::GetCornerRadius() const {
return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
views::EMPHASIS_MAXIMUM, GetContentsBounds().size());
}
void NewTabButton::GetBorderPath(float button_y,
float scale,
bool extend_to_top,
SkPath* path) const {
const gfx::Rect contents_bounds = GetContentsBounds();
if (MD::IsRefreshUi()) {
path->addRect(0, extend_to_top ? 0 : button_y,
contents_bounds.width() * scale,
button_y + contents_bounds.height() * scale);
return;
}
*path =
MD::IsTouchOptimizedUiEnabled()
? GetNewerMaterialUiButtonPath(button_y, scale, extend_to_top, false)
: GetMaterialUiButtonPath(button_y, contents_bounds.height(), scale,
extend_to_top, false);
}
void NewTabButton::PaintFill(bool pressed,
float scale,
const SkPath& fill,
gfx::Canvas* canvas) const {
gfx::ScopedCanvas scoped_canvas(canvas);
canvas->UndoDeviceScaleFactor();
cc::PaintFlags flags;
flags.setAntiAlias(true);
// For unpressed buttons, draw the fill and its shadow.
// Note that for newer UI, we always draw the fill since the button
// has a flat design. Hover highlights are handled by the ink drop.
const bool is_newer_ui = MD::IsNewerMaterialUi();
if (is_newer_ui || !pressed) {
const bool non_refresh_touch_ui =
MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
// First we compute the background image coordinates and scale, in case we
// need to draw a custom background image.
const ui::ThemeProvider* tp = GetThemeProvider();
bool has_custom_image;
const int bg_id = GetButtonFillResourceIdIfAny(
tab_strip_, tp, non_refresh_touch_ui, &has_custom_image);
if (has_custom_image && !new_tab_promo_observer_.IsObservingSources()) {
// For non-refresh touch UI, the background is that of the active tab, so
// the positioning must match that in Tab::PaintTab().
const int offset_y = non_refresh_touch_ui ? -Tab::GetStrokeHeight() : 0;
// The new tab background is mirrored in RTL mode, but the theme
// background should never be mirrored. Mirror it here to compensate.
float x_scale = 1.0f;
const gfx::Rect& contents_bounds = GetContentsBounds();
int x = GetMirroredX() + contents_bounds.x() + background_offset_;
if (base::i18n::IsRTL()) {
x_scale = -1.0f;
// Offset by |width| such that the same region is painted as if there
// was no flip.
x += contents_bounds.width();
}
const bool succeeded = canvas->InitPaintFlagsForTiling(
*tp->GetImageSkiaNamed(bg_id), x, GetContentsBounds().y() + offset_y,
x_scale * scale, scale, 0, 0, &flags);
DCHECK(succeeded);
} else {
flags.setColor(GetButtonFillColor());
}
cc::PaintFlags shadow_flags = flags;
// For Refresh, don't draw a shadow.
if (!MD::IsRefreshUi()) {
const SkColor stroke_color = tab_strip_->GetToolbarTopSeparatorColor();
const SkAlpha alpha = static_cast<SkAlpha>(
std::round(SkColorGetA(stroke_color) * 0.59375f));
shadow_flags.setLooper(
CreateShadowDrawLooper(SkColorSetA(stroke_color, alpha)));
}
canvas->DrawPath(fill, shadow_flags);
if (is_newer_ui) {
// We don't have hover/pressed states in the newer UI design.
// Instead we are using an ink drop effect.
return;
}
}
// Draw a white highlight on hover.
const SkAlpha hover_alpha =
static_cast<SkAlpha>(hover_animation().CurrentValueBetween(0x00, 0x4D));
if (hover_alpha != SK_AlphaTRANSPARENT) {
flags.setColor(SkColorSetA(SK_ColorWHITE, hover_alpha));
canvas->DrawPath(fill, flags);
}
// Most states' opacities are adjusted using an opacity recorder in
// TabStrip::PaintChildren(), but the pressed state is excluded there and
// instead rendered using a dark overlay here. Avoiding the use of the
// opacity recorder keeps the stroke more visible in this state.
if (pressed) {
flags.setColor(SkColorSetA(SK_ColorBLACK, 0x14));
canvas->DrawPath(fill, flags);
}
}
void NewTabButton::PaintPlusIcon(gfx::Canvas* canvas, int offset, int size) {
constexpr int kStrokeWidth = 2;
constexpr int kCapRadius = kStrokeWidth / 2;
cc::PaintFlags paint_flags;
paint_flags.setAntiAlias(true);
paint_flags.setColor(GetIconColor());
paint_flags.setStrokeCap(cc::PaintFlags::kRound_Cap);
paint_flags.setStrokeWidth(kStrokeWidth);
// With a round end-cap, the apparent line length will extend past the end
// points by one radius of the cap. Reduce the specified length to take this
// into account.
const int start = offset + kCapRadius;
const int end = offset + size - kCapRadius;
const int center = offset + size / 2;
// Draw the horizontal leg of the plus (+) icon
canvas->DrawLine(gfx::PointF(start, center), gfx::PointF(end, center),
paint_flags);
// Draw the vertical leg of the plus (+) icon
canvas->DrawLine(gfx::PointF(center, start), gfx::PointF(center, end),
paint_flags);
}
SkColor NewTabButton::GetButtonFillColor() const {
if (new_tab_promo_observer_.IsObservingSources()) {
return GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_ProminentButtonColor);
}
return tab_strip_->GetTabBackgroundColor(TAB_INACTIVE);
}
SkColor NewTabButton::GetIconColor() const {
return tab_strip_->GetTabForegroundColor(MD::IsRefreshUi() ? TAB_INACTIVE
: TAB_ACTIVE);
}
void NewTabButton::InitIncognitoIcon() {
DCHECK(ShouldDrawIncognitoIcon());
incognito_icon_ =
gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, GetIconColor());
}
SkPath NewTabButton::GetNewerMaterialUiButtonPath(float button_y,
float scale,
bool extend_to_top,
bool for_fill) const {
DCHECK(MD::IsNewerMaterialUi());
const float radius = GetCornerRadius() * scale;
const float rect_width =
2 * radius +
(ShouldDrawIncognitoIcon()
? scale * (incognito_icon_.width() + kDistanceBetweenIcons)
: 0);
const SkRect button_rect =
SkRect::MakeXYWH(0, button_y, rect_width, 2 * radius);
SkRRect rrect = SkRRect::MakeRectXY(button_rect, radius, radius);
// Inset by 1px for a fill path to give room for the stroke to show up. The
// stroke width is 1px regardless of the device scale factor.
if (for_fill)
rrect.inset(kStrokeThickness, kStrokeThickness);
SkPath path;
path.addRRect(rrect, SkPath::kCW_Direction);
if (extend_to_top) {
SkPath extension_path;
extension_path.addRect(
SkRect::MakeXYWH(0, 0, rect_width, button_y + radius),
SkPath::kCW_Direction);
Op(path, extension_path, kUnion_SkPathOp, &path);
}
path.close();
return path;
}
SkPath NewTabButton::GetMaterialUiButtonPath(int button_y,
int button_height,
float scale,
bool extend_to_top,
bool for_fill) const {
const float inverse_slope = Tab::GetInverseDiagonalSlope();
float bottom = (button_height - 2) * scale;
const float diag_height = bottom - 3.5 * scale;
const float diag_width = diag_height * inverse_slope;
const float right = diag_width + 4 * scale;
const int stroke_thickness = for_fill ? 0 : kStrokeThickness;
bottom += button_y + stroke_thickness;
SkPath path;
path.moveTo(right - stroke_thickness, bottom);
path.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale,
-1.5 * scale);
path.rLineTo(-diag_width, -diag_height);
if (extend_to_top) {
// Create the vertical extension by extending the side diagonals at the
// upper left and lower right corners until they reach the top and bottom of
// the border, respectively (in other words, "un-round-off" those corners
// and turn them into sharp points). Then extend upward from the corner
// points to the top of the bounds.
const float dy = scale + stroke_thickness * 2;
const float dx = inverse_slope * dy;
path.rLineTo(-dx, -dy);
path.rLineTo(0, -button_y - scale + stroke_thickness);
path.lineTo((width() - 2) * scale + stroke_thickness + dx, 0);
path.rLineTo(0, bottom);
} else {
if (for_fill) {
path.rCubicTo(0, -0.5 * scale, 0.25 * scale, -scale, scale, -scale);
} else {
path.rCubicTo(-0.5 * scale, -1.125 * scale, 0.5 * scale,
-scale - 2 * stroke_thickness, scale,
-scale - 2 * stroke_thickness);
}
path.lineTo((width() - 4) * scale - diag_width + stroke_thickness,
button_y + scale - stroke_thickness);
path.rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale,
1.5 * scale);
path.rLineTo(diag_width, diag_height);
if (for_fill) {
path.rCubicTo(0, 0.5 * scale, -0.25 * scale, scale, -scale, scale);
} else {
path.rCubicTo(0.5 * scale, 1.125 * scale, -0.5 * scale,
scale + 2 * stroke_thickness, -scale,
scale + 2 * stroke_thickness);
}
}
path.close();
return path;
}
void NewTabButton::UpdateInkDropBaseColor() {
DCHECK(MD::IsNewerMaterialUi());
set_ink_drop_base_color(color_utils::BlendTowardOppositeLuma(
GetButtonFillColor(), SK_AlphaOPAQUE));
}