| Index: chrome/browser/ui/views/tabs/tab.cc
|
| diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
|
| index f4fcad526683015f82d81f4f68578675cd0695f4..87250a99a5f6823cbc614d8db28bf3abeb6b1678 100644
|
| --- a/chrome/browser/ui/views/tabs/tab.cc
|
| +++ b/chrome/browser/ui/views/tabs/tab.cc
|
| @@ -26,9 +26,11 @@
|
| #include "grit/components_scaled_resources.h"
|
| #include "grit/theme_resources.h"
|
| #include "third_party/skia/include/effects/SkGradientShader.h"
|
| +#include "third_party/skia/include/pathops/SkPathOps.h"
|
| #include "ui/accessibility/ax_view_state.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
| #include "ui/base/models/list_selection_model.h"
|
| +#include "ui/base/resource/material_design/material_design_controller.h"
|
| #include "ui/base/resource/resource_bundle.h"
|
| #include "ui/base/theme_provider.h"
|
| #include "ui/gfx/animation/animation_container.h"
|
| @@ -41,6 +43,7 @@
|
| #include "ui/gfx/image/image_skia_operations.h"
|
| #include "ui/gfx/paint_vector_icon.h"
|
| #include "ui/gfx/path.h"
|
| +#include "ui/gfx/scoped_canvas.h"
|
| #include "ui/gfx/skia_util.h"
|
| #include "ui/gfx/vector_icons_public.h"
|
| #include "ui/resources/grit/ui_resources.h"
|
| @@ -436,6 +439,7 @@ Tab::ImageCacheEntry::~ImageCacheEntry() {}
|
|
|
| // static
|
| const char Tab::kViewClassName[] = "Tab";
|
| +const SkColor Tab::kInactiveTabColor = SkColorSetRGB(0xD0, 0xD0, 0xD0);
|
| Tab::TabImages Tab::active_images_ = {0};
|
| Tab::TabImages Tab::inactive_images_ = {0};
|
| Tab::TabImages Tab::mask_images_ = {0};
|
| @@ -648,10 +652,17 @@ int Tab::GetWidthOfLargestSelectableRegion() const {
|
| }
|
|
|
| gfx::Size Tab::GetMinimumInactiveSize() {
|
| - // Since we use images, the real minimum height of the image is
|
| - // defined most accurately by the height of the end cap images.
|
| - InitTabResources();
|
| - int height = active_images_.image_l->height();
|
| + int height;
|
| + if (ui::MaterialDesignController::IsModeMaterial()) {
|
| + const int kTabHeight = 29;
|
| + height = kTabHeight;
|
| + } else {
|
| + // Since we use images, the real minimum height of the image is
|
| + // defined most accurately by the height of the end cap images.
|
| + InitTabResources();
|
| + height = active_images_.image_l->height();
|
| + }
|
| +
|
| return gfx::Size(GetLayoutInsets(TAB).width(), height);
|
| }
|
|
|
| @@ -765,51 +776,58 @@ bool Tab::GetHitTestMask(gfx::Path* mask) const {
|
| const bool extend_to_top =
|
| widget && (widget->IsMaximized() || widget->IsFullscreen());
|
|
|
| - // Hit mask constants.
|
| - const SkScalar kTabCapWidth = 15;
|
| - const SkScalar kTabTopCurveWidth = 4;
|
| - const SkScalar kTabBottomCurveWidth = 3;
|
| + if (ui::MaterialDesignController::IsModeMaterial()) {
|
| + SkPath border;
|
| + const float scale = GetWidget()->GetCompositor()->device_scale_factor();
|
| + GetBorderPath(scale, extend_to_top, &border);
|
| + mask->addPath(border, SkMatrix::MakeScale(1 / scale));
|
| + } else {
|
| + // Hit mask constants.
|
| + const SkScalar kTabCapWidth = 15;
|
| + const SkScalar kTabTopCurveWidth = 4;
|
| + const SkScalar kTabBottomCurveWidth = 3;
|
| #if defined(OS_MACOSX)
|
| - // Mac's Cocoa UI doesn't have shadows.
|
| - const SkScalar kTabInset = 0;
|
| + // Mac's Cocoa UI doesn't have shadows.
|
| + const SkScalar kTabInset = 0;
|
| #elif defined(TOOLKIT_VIEWS)
|
| - // The views browser UI has shadows in the left, right and top parts of the
|
| - // tab.
|
| - const SkScalar kTabInset = 6;
|
| + // The views browser UI has shadows in the left, right and top parts of the
|
| + // tab.
|
| + const SkScalar kTabInset = 6;
|
| #endif
|
|
|
| - SkScalar left = kTabInset;
|
| - SkScalar top = GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT);
|
| - SkScalar right = SkIntToScalar(width()) - kTabInset;
|
| - SkScalar bottom = SkIntToScalar(height());
|
| + SkScalar left = kTabInset;
|
| + SkScalar top = GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT);
|
| + SkScalar right = SkIntToScalar(width()) - kTabInset;
|
| + SkScalar bottom = SkIntToScalar(height());
|
|
|
| - // Start in the lower-left corner.
|
| - mask->moveTo(left, bottom);
|
| + // Start in the lower-left corner.
|
| + mask->moveTo(left, bottom);
|
|
|
| - // Left end cap.
|
| - mask->lineTo(left + kTabBottomCurveWidth, bottom - kTabBottomCurveWidth);
|
| - mask->lineTo(left + kTabCapWidth - kTabTopCurveWidth,
|
| - top + kTabTopCurveWidth);
|
| - mask->lineTo(left + kTabCapWidth, top);
|
| + // Left end cap.
|
| + mask->lineTo(left + kTabBottomCurveWidth, bottom - kTabBottomCurveWidth);
|
| + mask->lineTo(left + kTabCapWidth - kTabTopCurveWidth,
|
| + top + kTabTopCurveWidth);
|
| + mask->lineTo(left + kTabCapWidth, top);
|
|
|
| - // Extend over the top shadow area if we have one and the caller wants it.
|
| - if (top > 0 && extend_to_top) {
|
| - mask->lineTo(left + kTabCapWidth, 0);
|
| - mask->lineTo(right - kTabCapWidth, 0);
|
| - }
|
| + // Extend over the top shadow area if we have one and the caller wants it.
|
| + if (top > 0 && extend_to_top) {
|
| + mask->lineTo(left + kTabCapWidth, 0);
|
| + mask->lineTo(right - kTabCapWidth, 0);
|
| + }
|
|
|
| - // Connect to the right cap.
|
| - mask->lineTo(right - kTabCapWidth, top);
|
| + // Connect to the right cap.
|
| + mask->lineTo(right - kTabCapWidth, top);
|
|
|
| - // Right end cap.
|
| - mask->lineTo(right - kTabCapWidth + kTabTopCurveWidth,
|
| - top + kTabTopCurveWidth);
|
| - mask->lineTo(right - kTabBottomCurveWidth, bottom - kTabBottomCurveWidth);
|
| - mask->lineTo(right, bottom);
|
| + // Right end cap.
|
| + mask->lineTo(right - kTabCapWidth + kTabTopCurveWidth,
|
| + top + kTabTopCurveWidth);
|
| + mask->lineTo(right - kTabBottomCurveWidth, bottom - kTabBottomCurveWidth);
|
| + mask->lineTo(right, bottom);
|
|
|
| - // Close out the path.
|
| - mask->lineTo(left, bottom);
|
| - mask->close();
|
| + // Close out the path.
|
| + mask->lineTo(left, bottom);
|
| + mask->close();
|
| + }
|
|
|
| // It is possible for a portion of the tab to be occluded if tabs are
|
| // stacked, so modify the hit test mask to only include the visible
|
| @@ -1240,15 +1258,26 @@ void Tab::PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas) {
|
| }
|
| SkPoint p;
|
| p.set(SkDoubleToScalar(x), 0);
|
| - gfx::Canvas background_canvas(size(), canvas->image_scale(), false);
|
| - PaintInactiveTabBackground(&background_canvas);
|
| - gfx::ImageSkia background_image(background_canvas.ExtractImageRep());
|
| - canvas->DrawImageInt(background_image, 0, 0);
|
| - gfx::Canvas hover_canvas(size(), canvas->image_scale(), false);
|
| - DrawHighlight(&hover_canvas, p, SkFloatToScalar(radius), alpha);
|
| - gfx::ImageSkia hover_image = gfx::ImageSkiaOperations::CreateMaskedImage(
|
| - gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image);
|
| - canvas->DrawImageInt(hover_image, 0, 0);
|
| + if (ui::MaterialDesignController::IsModeMaterial()) {
|
| + PaintInactiveTabBackground(canvas);
|
| + gfx::ScopedCanvas scoped_canvas(canvas);
|
| + const float scale = canvas->UndoDeviceScaleFactor();
|
| + SkPath fill;
|
| + GetFillPath(scale, &fill);
|
| + canvas->ClipPath(fill, true);
|
| + p.scale(SkFloatToScalar(scale));
|
| + DrawHighlight(canvas, p, SkFloatToScalar(radius * scale), alpha);
|
| + } else {
|
| + gfx::Canvas background_canvas(size(), canvas->image_scale(), false);
|
| + PaintInactiveTabBackground(&background_canvas);
|
| + gfx::ImageSkia background_image(background_canvas.ExtractImageRep());
|
| + canvas->DrawImageInt(background_image, 0, 0);
|
| + gfx::Canvas hover_canvas(size(), canvas->image_scale(), false);
|
| + DrawHighlight(&hover_canvas, p, SkFloatToScalar(radius), alpha);
|
| + gfx::ImageSkia hover_image = gfx::ImageSkiaOperations::CreateMaskedImage(
|
| + gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image);
|
| + canvas->DrawImageInt(hover_image, 0, 0);
|
| + }
|
| }
|
|
|
| void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) {
|
| @@ -1303,31 +1332,75 @@ void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas,
|
| SkPoint hover_location(PointToSkPoint(hover_controller_.location()));
|
| const SkAlpha hover_alpha = hover_controller_.GetAlpha();
|
|
|
| - if (draw_hover) {
|
| - // Draw everything to a temporary canvas so we can extract an image for use
|
| - // in masking the hover glow.
|
| - gfx::Canvas background_canvas(size(), canvas->image_scale(), false);
|
| - PaintTabFill(&background_canvas, fill_image, x_offset, y_offset, is_active);
|
| - gfx::ImageSkia background_image(background_canvas.ExtractImageRep());
|
| - canvas->DrawImageInt(background_image, 0, 0);
|
| + if (ui::MaterialDesignController::IsModeMaterial()) {
|
| + gfx::ScopedCanvas scoped_canvas(canvas);
|
| + const float scale = canvas->UndoDeviceScaleFactor();
|
| +
|
| + // Draw the fill.
|
| + SkPath fill;
|
| + GetFillPath(scale, &fill);
|
| + SkPaint paint;
|
| + paint.setAntiAlias(true);
|
| + {
|
| + gfx::ScopedCanvas clip_scoper(canvas);
|
| + canvas->ClipPath(fill, true);
|
| + if (has_custom_image) {
|
| + gfx::ScopedCanvas scale_scoper(canvas);
|
| + canvas->sk_canvas()->scale(scale, scale);
|
| + canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, width(),
|
| + height());
|
| + } else {
|
| + paint.setColor(
|
| + is_active ? SkColorSetRGB(0xF2, 0xF2, 0xF2) : kInactiveTabColor);
|
| + canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale),
|
| + paint);
|
| + }
|
| + if (draw_hover) {
|
| + hover_location.scale(SkFloatToScalar(scale));
|
| + DrawHighlight(canvas, hover_location, radius * scale, hover_alpha);
|
| + }
|
| + }
|
|
|
| - gfx::Canvas hover_canvas(size(), canvas->image_scale(), false);
|
| - DrawHighlight(&hover_canvas, hover_location, radius, hover_alpha);
|
| - gfx::ImageSkia result = gfx::ImageSkiaOperations::CreateMaskedImage(
|
| - gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image);
|
| - canvas->DrawImageInt(result, 0, 0);
|
| + // Draw the stroke.
|
| + SkPath stroke;
|
| + GetBorderPath(scale, false, &stroke);
|
| + Op(stroke, fill, kDifference_SkPathOp, &stroke);
|
| + if (!is_active) {
|
| + // Clip out the bottom line; this will be drawn for us by
|
| + // TabStrip::PaintChildren().
|
| + canvas->sk_canvas()->clipRect(
|
| + SkRect::MakeWH(width() * scale, height() * scale - 1));
|
| + }
|
| + paint.setARGB(0x40, 0x00, 0x00, 0x00);
|
| + canvas->DrawPath(stroke, paint);
|
| } else {
|
| - PaintTabFill(canvas, fill_image, x_offset, y_offset, is_active);
|
| - }
|
| + if (draw_hover) {
|
| + // Draw everything to a temporary canvas so we can extract an image for
|
| + // use in masking the hover glow.
|
| + gfx::Canvas background_canvas(size(), canvas->image_scale(), false);
|
| + PaintTabFill(&background_canvas, fill_image, x_offset, y_offset,
|
| + is_active);
|
| + gfx::ImageSkia background_image(background_canvas.ExtractImageRep());
|
| + canvas->DrawImageInt(background_image, 0, 0);
|
| +
|
| + gfx::Canvas hover_canvas(size(), canvas->image_scale(), false);
|
| + DrawHighlight(&hover_canvas, hover_location, radius, hover_alpha);
|
| + gfx::ImageSkia result = gfx::ImageSkiaOperations::CreateMaskedImage(
|
| + gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image);
|
| + canvas->DrawImageInt(result, 0, 0);
|
| + } else {
|
| + PaintTabFill(canvas, fill_image, x_offset, y_offset, is_active);
|
| + }
|
|
|
| - // Now draw the stroke, highlights, and shadows around the tab edge.
|
| - TabImages* stroke_images = is_active ? &active_images_ : &inactive_images_;
|
| - canvas->DrawImageInt(*stroke_images->image_l, 0, 0);
|
| - canvas->TileImageInt(
|
| - *stroke_images->image_c, stroke_images->l_width, 0,
|
| - width() - stroke_images->l_width - stroke_images->r_width, height());
|
| - canvas->DrawImageInt(*stroke_images->image_r,
|
| - width() - stroke_images->r_width, 0);
|
| + // Now draw the stroke, highlights, and shadows around the tab edge.
|
| + TabImages* stroke_images = is_active ? &active_images_ : &inactive_images_;
|
| + canvas->DrawImageInt(*stroke_images->image_l, 0, 0);
|
| + canvas->TileImageInt(
|
| + *stroke_images->image_c, stroke_images->l_width, 0,
|
| + width() - stroke_images->l_width - stroke_images->r_width, height());
|
| + canvas->DrawImageInt(*stroke_images->image_r,
|
| + width() - stroke_images->r_width, 0);
|
| + }
|
| }
|
|
|
| void Tab::PaintTabFill(gfx::Canvas* canvas,
|
| @@ -1543,6 +1616,66 @@ void Tab::ScheduleIconPaint() {
|
| SchedulePaintInRect(bounds);
|
| }
|
|
|
| +void Tab::GetFillPath(float scale, SkPath* fill) const {
|
| + const float right = width() * scale;
|
| + const float bottom = height() * scale;
|
| +
|
| + fill->moveTo(right - 1, bottom);
|
| + fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale,
|
| + -1.5 * scale);
|
| + fill->lineTo(right - 1 - 13.5 * scale, 2.5 * scale);
|
| + // Prevent overdraw in the center near minimum width (only happens if
|
| + // scale < 2). We could instead avoid this by increasing the tab inset
|
| + // values, but that would shift all the content inward as well, unless we
|
| + // then overlapped the content on the endcaps, by which point we'd have a
|
| + // huge mess.
|
| + const float total_endcap_width = 31 * scale + 2;
|
| + const float overlap = total_endcap_width - right;
|
| + const float offset = (overlap > 0) ? (overlap / 2) : 0;
|
| + fill->rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset,
|
| + -1.5 * scale, -2 * scale + offset, -1.5 * scale);
|
| + if (overlap < 0)
|
| + fill->lineTo(1 + 15.5 * scale, scale);
|
| + fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale,
|
| + -2 * scale - offset, 1.5 * scale);
|
| + fill->lineTo(1 + 2 * scale, bottom - 1.5 * scale);
|
| + fill->rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale,
|
| + 1.5 * scale);
|
| + fill->close();
|
| +}
|
| +
|
| +void Tab::GetBorderPath(float scale, bool extend_to_top, SkPath* path) const {
|
| + const float top = scale - 1;
|
| + const float right = width() * scale;
|
| + const float bottom = height() * scale;
|
| +
|
| + path->moveTo(0, bottom);
|
| + path->rLineTo(0, -1);
|
| + path->rCubicTo(0.75 * scale, 0, 1.625 * scale, -0.5 * scale, 2 * scale,
|
| + -1.5 * scale);
|
| + path->lineTo(13.5 * scale, top + 1.5 * scale);
|
| + if (extend_to_top) {
|
| + // Create the vertical extension by extending the side diagonals until they
|
| + // reach the top of the bounds.
|
| + const float dy = 2.5 * scale - 1;
|
| + const float dx = 11.5 / 25 * dy;
|
| + path->rLineTo(dx, -dy);
|
| + path->lineTo(right - 13.5 * scale - dx, 0);
|
| + path->rLineTo(dx, dy);
|
| + } else {
|
| + path->rCubicTo(0.375 * scale, -scale, 1.25 * scale, -1.5 * scale, 2 * scale,
|
| + -1.5 * scale);
|
| + path->lineTo(right - 15.5 * scale, top);
|
| + path->rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale,
|
| + 1.5 * scale);
|
| + }
|
| + path->lineTo(right - 2 * scale, bottom - 1 - 1.5 * scale);
|
| + path->rCubicTo(0.375 * scale, scale, 1.25 * scale, 1.5 * scale, 2 * scale,
|
| + 1.5 * scale);
|
| + path->rLineTo(0, 1);
|
| + path->close();
|
| +}
|
| +
|
| gfx::Rect Tab::GetImmersiveBarRect() const {
|
| // The main bar is as wide as the normal tab's horizontal top line.
|
| // This top line of the tab extends a few pixels left and right of the
|
|
|